bash 使用verbose和xtrace(set -vx)记录bash脚本并自定义PS4

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14639125/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 04:24:42  来源:igfitidea点击:

logging bash scripts with verbose and xtrace (set -vx) and customizing PS4

bashdebuggingshell

提问by Jason

I am trying to configure PS4 for better xtrace output when I also have verbose set for verbose mode. Several of the things I would like to do spawn subshells but I want to see any -x or -v output for those operations because they would print for every line.

当我还为详细模式设置了详细设置时,我正在尝试配置 PS4 以获得更好的 xtrace 输出。我想做的几件事会产生子外壳,但我想看到这些操作的任何 -x 或 -v 输出,因为它们会为每一行打印。

I also wanted to get rid of the nesting/indirection depth level indicator (+) because I want the trace output to be aligned.

我还想摆脱嵌套/间接深度级别指示器 (+),因为我希望跟踪输出对齐。

My initial idea was:

我最初的想法是:

BACKSPACES=$'\b\b\b\b\b\b\b\b'
PS4='+`printf "%s[\t] %-16.16s:%03d (%-16.16s) $ " ${BACKSPACES:0:$BASH_SUBSHELL} $(basename $BASH_SOURCE) $LINENO ${FUNCNAME[0]:+${FUNCNAME[0]}}`'

The first problem I ran into was that $BASH_SUBSHELL doesn't always seem to be the same as the number of +'s that would print. Looking into $SHLVL didn't help either.

我遇到的第一个问题是 $BASH_SUBSHELL 似乎并不总是与要打印的 + 的数量相同。查看 $SHLVL 也无济于事。

echo $X
+ 0 2 $ echo x <-- toplevel prints one + and $BASH_SUBSHELL is 0, as expected
x

( echo subshell )
+ 1 2 $ echo subshell <-- $BASH_SUBSHELL increments to 1 as expected, but why only one + instead of two ++?

source ./test2.sh
+ 0 2 $ source ./test2.sh
echo $X$X$X$X
++ 0 2 $ echo xxxx <-- ??? is $BASH_SUBSHELL relative to the current file or something but the + indicators are not???
xxxx
subshell

I think I have gotten around this by forgetting about using $BASH_SUBSHELL and instead making the first character in PS4 an unprintable one but I'd still like to know why $BASH_SUBSHELL isn't what I expect.

我想我已经通过忘记使用 $BASH_SUBSHELL 而是使 PS4 中的第一个字符成为不可打印的字符来解决这个问题的,但我仍然想知道为什么 $BASH_SUBSHELL 不是我所期望的。

To work around the issue with subshells being created within PS4, I looked an equivalent of PS1's PROMPT_COMMAND but didn't find anything except some indications on how to implement it myself.

为了解决在 PS4 中创建子外壳的问题,我查看了 PS1 的 PROMPT_COMMAND 的等价物,但除了有关如何自己实现它的一些指示外,没有找到任何东西。

The best way I thought I found was to trap the DEBUG signal.

我认为我发现的最好方法是捕获 DEBUG 信号。

trap 'debugfun $BASH_SOURCE' DEBUG

Of course, the -vx options also apply to calls to debugfun. My solution to suppress that output was to wrap the function's steps in set +vx and set -vx.

当然,-vx 选项也适用于对 debugfun 的调用。我抑制该输出的解决方案是将函数的步骤包装在 set +vx 和 set -vx 中。

debugfun() {
  set +vx
  VAR=`basename  .sh`
  set -vx
}

Now... I had to deal with the matter of every call printing:

现在...我不得不处理每次调用打印的问题:

debugfun $BASH_SOURCE <-- from -v
++ debugfun ./test.sh <-- from -x
++ set +vx <-- from -x

I'm not sure why -v doesn't cause a print on the "set +vx" line either. I thought -T option might do that but it didn't.

我不确定为什么 -v 也不会在“set +vx”行上打印。我认为 -T 选项可能会这样做,但事实并非如此。

Anyway, I thought the output was always going to consistent so all I had to do was erase those 3 lines from within debugfun. My ugly solution was to add this line after the "set +vx":

无论如何,我认为输出总是一致的,所以我所要做的就是从 debugfun 中擦除这 3 行。我丑陋的解决方案是在“set +vx”之后添加这一行:

printf "\b\r3[K\b\r3[K\b\r3[K" # clear previous 3 lines

It worked!

有效!

... Except when I pipe more than once.

......除非我不止一次地吸管。

I'll remove the printf line to show why:

我将删除 printf 行以说明原因:

echo $X$X | sed 's/x/y/' # pipe once... debugfun is called every time before each pipe component
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
+ echo xx
+ sed s/x/y/

echo $X$X$X | sed 's/x/y/' | sed 's/y/x/' # pipe twice 
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
+ echo xxx 
+ sed s/x/y/ # because this line is here, clearing 3 previous lines from the next "set +vx" would clear it
debugfun $BASH_SOURCE
++ debugfun ./test.sh
++ set +vx
+ sed s/y/x/
xxx

I did, finally, come up with this solution but I think it's really ugly and, while close, isn't exactly what I want:

最后,我确实想出了这个解决方案,但我认为它真的很丑,虽然很接近,但并不是我想要的:

SPACES="                        "
PS4='^@\[\e[31m\]+ [\t] \
${SPACES:0:$((${#BASH_SOURCE} > 24 ? 0 : 24 - ${#BASH_SOURCE}))}${BASH_SOURCE:$((${#BASH_SOURCE} < 24 ? 0 : -24))}:\
${SPACES:0:$((3-${#LINENO}))}$LINENO \
${FUNCNAME:-${SPACES:0:20}}${FUNCNAME:+${SPACES:0:$((20 - ${#FUNCNAME}))}} \
$ \[\e[0m\]'

The main thing I don't like is that I can't nest the bash string manipulations to not only reverse truncate $BASH_SOURCE, but to get it's basename without a subshell (i.e. combine with echo ${BASH_SOURCE##*/})

我不喜欢的主要事情是我不能嵌套 bash 字符串操作,不仅要反向截断 $BASH_SOURCE,还要在没有子shell 的情况下获得它的基本名称(即与 echo ${BASH_SOURCE##*/} 结合)

I'm out of ideas and would appreciate and clarification or tips on how to accomplish what I'm trying to do.

我没有想法,希望得到关于如何完成我正在尝试做的事情的澄清或提示。

GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)

回答by spbnick

I'd suggest forgoing PS4 and doing all trace output in a DEBUG trap. Most, if not all the information -x prints is available elsewhere. See BASH_SOURCE, BASH_LINENO, FUNCNAME and BASH_COMMAND.

我建议放弃 PS4 并在调试陷阱中进行所有跟踪输出。-x 打印的大多数(如果不是全部)信息都可以在其他地方获得。请参见 BASH_SOURCE、BASH_LINENO、FUNCNAME 和 BASH_COMMAND。

Like this:

像这样:

function trace()
{
    echo "TRACE" \
         "${BASH_SOURCE[1]}:${BASH_LINENO[0]}:${FUNCNAME[1]}:" \
         "$BASH_COMMAND"
}

set -o functrace
shopt -s extdebug
trap trace DEBUG