Bash:执行前突出显示命令(set -x)

7
我有一个 Bash 脚本,其中包含大约20个命令,为了调试目的,我经常需要滚动查看输出。不幸的是,Bash 不会告诉我输出的哪个部分属于哪个命令。 当我在脚本中使用“set -x”时,它至少会打印一些有关它刚刚执行了什么的信息,但我并不喜欢它生成的输出。
例如,如果我有这个脚本:
#!/bin/bash

set -x
echo "foo"
if [ "$ASD" == "QWE" ] ; then
    echo "bar"
fi

我希望输出的结果类似于这样:

echo "foo"
foo
echo "bar"
bar

或者可能是这样的:

echo "foo"
foo
if [ "value_of_ASD" == "QWE" ] ; then
echo "bar"
bar
fi

而不是在加粗的命令前面打印"+"字符,我也不喜欢if语句出现像'[' value_of_ASD == QWE ']'这样的情况。
如何使用bash实现这个目标?
此时输出看起来像这样:
+ echo foo
foo
+ '[' value_of_ASD == QWE ']'
+ echo bar
bar

编辑:
我有一个想法,就是编写一个脚本,在主脚本的一开始引用它,然后让被引用的脚本解析主脚本。类似这样:
source_me.sh
#!/bin/bash
SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/$(basename $0)"
FORMAT_SET_BOLD='\033[0;1m'
FORMAT_RESET='\033[0m'

cat $SCRIPT_PATH | tail -n "+$START_LINE" | while read line; do
    printf "${FORMAT_SET_BOLD}${line}${FORMAT_RESET}\n"
    eval "${line}"
done

exit 0;

main.sh

#!/bin/bash
START_LINE=$((LINENO+1)) source ./source_me.sh

echo "Foo"
echo "Bar"
echo "+Hello"

在这种情况下的输出为:
``` echo "Foo" Foo echo "Bar" Bar echo "+Hello" +Hello ```
但是,如果我使用更复杂的多行代码(例如if语句、循环等),这种方法将失败。
#!/bin/bash
START_LINE=$((LINENO+1)) source ./source_me.sh

echo "Foo"

if [ "$FOOBAR" == "" ] ; then
    echo "Bar"
fi

echo "+Hello"

在这种情况下,我得到了:
```

在这种情况下,我得到:

echo "Foo"
Foo

if [ "$FOOBAR" == "" ] ; then
./source_me.sh: eval: line 9: syntax error: unexpected end of file
echo "Bar"
Bar
fi
./source_me.sh: eval: line 8: syntax error near unexpected token 'fi'
./source_me.sh: eval: line 8: 'fi'

echo "+Hello"
+Hello

```

编写另一个脚本,您可以将输出导入其中,并使用终端仿真器将以“+”开头的行更改为粗体。 - lurker
正在执行的命令通常会产生包含“+”字符的输出。所以这不是一个选项。 - Forivin
我不相信除了编写一个执行@lurker建议的脚本之外,还有其他方法可以做到这一点。如果您能够通过查看set -x输出来解释它,那么您可以编写一个执行相同操作的脚本。但是这并不会很美观。 - JNevill
1
@Forivin 我完全理解。只是据我所知,没有办法更改 -x 输出的格式(至少没有内置的方法)。你可以编写一些轻量级的代码并将其管道传输到其中,它可能不完美,但它可能有助于减轻 set -x 的视觉疲劳,例如 ./yourscript.sh | awk '/^\+/{printf "%c[1;32m %s\n",27, $0} /^[^\+]/{printf "%c[1;30m %s\n",27,$0}',其中它将以绿色突出显示以 + 开头的行,并以可爱的灰色显示其余部分。 - JNevill
1
你可以随时获取bash源代码并进行修改。它是可用的。 :) - lurker
显示剩余6条评论
4个回答

10

我想对rubo77的回答进行补充,并提供一些我认为值得分开回答的示例:

纯文本前缀

所以基本示例是将PS4设置为一些纯文本,例如:

PS4="# "; set -x

这将导致:

颜色和额外行文本前缀

但是,由于您可以使用特殊字符和ANSI转义代码,因此例如您可以在每个新命令之前添加一个新行,并以彩色打印前缀,例如:
PS4="\n\033[1;33m>>>\033[0m "; set -x

结果:



动态颜色前缀

最后,您可以使命令前缀在每次使用时调用其他程序,您可以使用它添加时间戳,例如:

# yes, there have to be single quotes below, not double!
PS4='\033[1;34m$(date +%H:%M:%S)\033[0m '; set -x

结果:


2
您可以通过设置PS4来将+更改为您想要的字符串,例如:
PS4="# "; set -x

1
请注意,set -x 没有关于你的终端的内在知识,这意味着需要发送正确的字符来创建加粗或彩色文本。
如果你可以通过单个文件句柄捕获所有输出,那么你可能可以使用管道来实现此目的:
$ /path/to/your/script 2>&1 | sed "/^+ /s/.*/$(tput bold)&$(tput sgr0)/"

如果你想了解我们使用的工具如何实现加粗效果,可以查看man tput

这取决于许多我不知道你环境的因素。结果可能因人而异,可能包含坚果。


注意:
您可能认为最好使用类似以下一行代码单独捕获stderr:
$ /path/to/your/script 2> >(sed "/^+ /s/.*/$(tput bold)&$(tput sgr0)/")

哎呀,这个并不一致,因为通过管道传递的标准错误可能在脚本后续的标准输出之前没有提交到终端。还要注意,由于它使用进程替换,所以这个解决方案是bashism,并且不能在POSIX上移植。

但这会影响所有以+字符开头的行,对吗?如评论中已经提到的那样,这是有问题的,因为我使用的某些命令会打印以+字符开头的行。 - Forivin
是的,这显然是一个妥协。我在我的答案中提出的sed脚本匹配了行首的加号和空格,但这只是个小问题——对你的问题的真正回答是“抱歉,不能这样做”。每一个其他的答案(包括我的)都是一种hack。我经常使用的另一个选项是添加调试输出,通过一个选项(如-d)来触发,其中包括$LINENO或者${BASH_LINENO[@]}(如果可用)。但这不是你的问题。 - ghoti

0
你要找的是 set -v
-v 会打印出解释器刚刚读取的那一行。
-x 会打印出解释器刚刚读取的那一行的后解析结果。
文件 x:
set -vx
foo=1
echo $foo
set +vx

执行中:

$: . x
foo=1
++ foo=1
echo $foo
++ echo 1
1
set +vx
++ set +vx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接