如何在执行命令前打印每个命令?

367

如何设置Bash脚本以在执行之前打印每个命令?这对于调试非常有用。

我已经尝试过这种方法:

CMD="./my-command --params >stdout.txt 2>stderr.txt"
echo $CMD
`$CMD`

它应该先打印这个:

./my-command --params >stdout.txt 2>stderr.txt

然后执行./my-command --params,并将输出重定向到指定的文件。


1
如果你把\$CMD`改成"$CMD",我认为你会更幸运。当bash遇到`$CMD`时,它会用命令替换$CMD,然后你就会得到`./my-command --params >stdout.txt 2>stderr.txt``。由于这被包含在反引号中,bash将执行该命令,而不是将输出打印到标准输出,它将用包括反引号的表达式替换为命令的输出。由于这最终出现在命令行的开头,bash将尝试将输出解释为新命令,这不是你想要的。 - HelloGoodbye
@HelloGoodbye,实际上,甚至不要使用引号。有了引号,Bash 尝试将整个字符串解释为命令的名称;没有引号,Bash 会正确扩展它,并将其视为一个命令后跟参数、重定向等,就像您直接键入它时通常解释它一样。 - Ken Bellows
1
特别是设置PS4变量以定义作为跟踪输出命令前缀显示的提示符。请参考以下链接:https://www.thegeekstuff.com/2008/09/bash-shell-take-control-of-ps1-ps2-ps3-ps4-and-prompt_command/ - 0 _
@KenBellows,这是不正确的。未引用扩展与将其作为代码进行评估不同:引号不被识别,重定向不被识别——所有这些语法都被视为文字。只有字符串拆分和通配符扩展发生的解析步骤。这是[BashFAQ#50](http://mywiki.wooledge.org/BashFAQ/050)的核心主题。如果您希望引号/重定向/等被识别,可以使用`eval“$CMD”`,但这本身存在[严重的安全风险](http://mywiki.wooledge.org/BashFAQ/048),这就是为什么FAQ#50 _不_建议使用eval,而是教授数组和函数的使用原因。 - Charles Duffy
4个回答

491
set -o xtrace
或。
bash -x myscript.sh

如果我没记错的话,这也适用于标准/bin/sh(可能是POSIX的事情)

还要记住,有bashdbbash Shell Debugger, release 4.0-0.4


要恢复正常,请退出子shell或

set +o xtrace

2
完美,这里有更多关于此的信息(您还可以在该页面上找到更多有用的信息):Bash Hackers Wiki: Bash debugging tips - shell debug output - entropo
14
还要注意 bash -v / set -v,这稍微有些不同,但更加简洁。 - tripleee
3
这是POSIX:http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_25。 - Ciro Santilli OurBigBook.com
1
还有一种“逻辑括号”可能会很有用:OPT=$- 以保存所有键,然后使用 set -$OPT 进行恢复。 - Tomilov Anatoliy
1
详细说明 set -v,它会在变量替换之前打印 Bash 的原始行。 - Steve Bennett
显示剩余2条评论

162

set -x 是可以的,但如果你做了类似这样的事情:

set -x;
command;
set +x;

这将导致打印

+ command
+ set +x;

你可以使用子shell来防止这种情况,例如:

(set -x; command)

它只会打印该命令。


16
子Shell真是太棒了,谢谢! - Jujhar Singh

124

最简单的方法是让 bash 来完成:

set -x

或者直接运行 bash -x myscript


18
值得注意的是,回显的命令会发送到标准错误流(stderr)而不是标准输出流(stdout)。 - hertzsprung
如何在不将输出发送到stderr的情况下使用set x/-x? - grayaii
2
使用 BASH_XTRACEFD=1 打印到标准输出。为了避免 + 前缀,使用 PS4="\000" - Martin
请注意,BASH_XTRACEFD 仅适用于 bash 4.0 或更高版本(这意味着它不适用于 macOS 默认提供的 bash)。 - victorlin

43
"

set -x 是可以的。

另一种打印每个执行命令的方法是使用带有 DEBUGtrap。 在您的脚本开头放置此行:

"
trap 'echo "# $BASH_COMMAND"' DEBUG

你可以在这里找到很多其他的trap用法here


1
这是一种简洁的方式。 - PK.Shrestha
4
我更喜欢这个比 -x 更好,因为你可以像下面这样做: trap 'echo "# $(date) $BASH_COMMAND"' DEBUG 这将显示执行 Bash 脚本中每个命令所需的时间。 谢谢! - PK.Shrestha
5
巧妙的方法。对于未初始化的人(比如我自己),可以使用以下命令将trap重置回正常模式:trap - DEBUG - JJLL

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