从脚本外部抑制bash执行跟踪(设置-x)


17

我试图找到这个问题的答案,但到目前为止还没有运气:

我有一个运行其他脚本的脚本,其中许多其他脚本中都带有“ set -x”,这使它们可以打印执行的所有命令。我想摆脱它,但是如果任何脚本将错误消息发送到stderr,则保留信息。

所以我不能简单地写 ./script 2>/dev/null

另外,我没有编辑其他脚本的特权,因此无法手动更改set选项。

我正在考虑记录从stderr到单独文件的所有内容,并过滤掉跟踪命令,但是也许有更简单的方法吗?


1
./script 2>some_file
佐藤桂

Answers:


25

使用bash4.1及更高版本,您可以执行

BASH_XTRACEFD=7 ./script.bash 7> /dev/null

(当bash作为调用时也有效sh)。

基本上,我们要在文件描述符7而不是默认值2上bash输出xtrace输出,并将该文件描述符重定向到/dev/null。fd数是任意的。使用高于2的fd,否则脚本中不会使用该fd。如果您要在其中输入此命令的shell是bashyash,则您甚至可以使用大于9的数字(尽管如果文件描述符由shell内部使用,则可能会遇到问题)。

如果您bash从中调用该脚本的shell 是zsh,则还可以执行以下操作:

(export BASH_XTRACEFD; ./script.bash {BASH_XTRACEFD}> /dev/null)

为变量自动分配高于9的第一个自由fd。

对于较旧版本的bash,另一个选择是,如果xtrace使用set -x(相对于#! /bin/bash -xset -o xtrace)打开,则将其重新定义set为导出的函数,该函数在通过时不执行任何操作-x(尽管如果该脚本(或bash它调用的任何其他脚本)会中断该脚本)用于set设置位置参数)。

喜欢:

set()
  case $1 in
    (-x) return 0;;
    (-[!-]|"") builtin set "$@";;
    (*) echo >&2 That was a bad idea, try something else; builtin set "$@";;
  esac

export -f set
./script.bash

另一种选择是在每个命令之前执行的$BASH_ENV文件中添加DEBUG陷阱set +x

echo 'trap "{ set +x; } 2>/dev/null" DEBUG' > ~/.no-xtrace
BASH_ENV=~/.no-xtrace ./script.bash

但是,如果set -x在子外壳中完成,那将行不通。

正如@ilkkachu所说,只要您对文件系统上的任何文件夹具有写许可权,您至少应该能够复制该脚本并进行编辑。

如果无处可写脚本副本,或者每次对原始脚本进行更新时都不方便制作和编辑新副本,那么您仍然可以执行以下操作:

 bash <(sed 's/set -x/set +x/g' ./script.bash)

如果脚本进行任何花哨的操作$0或诸如此类的特殊变量$BASH_SOURCE(例如,查找与脚本本身位置相关的文件),则该方法(和复制方法)可能无法正常工作,因此您可能需要进行更多编辑,例如替换$0为脚本的路径...


您的第一个答案就是我所需要的,干净而优雅。您能解释一下它是如何工作的吗?为什么是7号?其他数字可以用来做什么?谢谢。
人类

@human,看到编辑
斯特凡Chazelas

1
{BASH_XTRACEFD}>技巧同样适用于bash4.1或更高版本。
chepner

@chepner,是的,根据zsh开发人员的建议,该功能已同时添加到zsh,ksh93和bash中。但是,在这里它不工作ksh93bash在该变量没有在命令(比较的环境中通过<shell> -c 'export fd; printenv fd {fd}> /dev/null'zshbashksh93)。您可以分两步进行操作,也可以使用来使它在ksh93/中工作,但如果xtrace选项处于启用状态,则会产生副作用。bashevalbash
斯特凡Chazelas

5

由于它们是脚本,因此您可以复制它们并进行编辑。

除此之外,过滤输出看起来很简单,您可以显式设置PS4为比单个加号更不寻常的内容,以使过滤更容易:

PS4="%%%%" bash script.sh 2>&1 | grep -ve '^%%%%'

(当然,这会使stdout和stdin崩溃,但是在Bash中仅对stderr进行管道处理会有点毛茸茸,因此我将忽略它)


1
管道只是标准错误很简单:PS4="%%%%" bash script.sh 2> >(grep -ve '^%%%%')
帕特里克

4
@Patrick,这样做是bash有问题的,因为它grep是异步运行的(bash没有等待它,因此它可以(而且经常)在脚本中的下一个命令启动后输出东西)。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.