我正在运行 Jenkins 的 shell 脚本,它使用 shebang 选项启动 shell 脚本#!/bin/sh -ex
。
根据适合傻瓜使用的 Bash Shebang?,,-x
“使 shell 打印执行跟踪”,这对于大多数目的都很有用 - 除了回声:
echo "Message"
产生输出
+ echo "Message"
Message
这有点多余,而且看起来有点奇怪。有没有办法保持-x
启用状态,但只输出
Message
而不是上面两行,例如通过在 echo 命令前加上特殊的命令字符,或者重定向输出?
答案1
当你被鳄鱼围困时,很容易忘记目标是排干沼泽。—— 俗话说
问题在于echo
,但到目前为止,大多数答案都集中在如何潜入命令set +x
上。有一个更简单、更直接的解决方案:
{ echo "Message"; } 2> /dev/null
{ …; } 2> /dev/null
(我承认,如果我没有在之前的答案中看到它,我可能不会想到它。)
这有点麻烦,但是,如果您有一组连续的echo
命令,则不需要对每个命令单独执行此操作:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
请注意,有换行符时不需要分号。
您可以使用以下方法减轻打字负担kenorb 的想法/dev/null
在非标准文件描述符(例如 3)上永久 打开,然后说2>&3
而不是2> /dev/null
一直说。
在撰写本文时,前四个答案需要做一些特殊的事情(并且在大多数情况下很麻烦)
每一个你做的时候echo
。如果你真的想全部 echo
命令来抑制执行跟踪(你为什么不这样做呢?),你可以在全局范围内这样做,而不需要处理大量代码。首先,我注意到别名没有被跟踪:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(请注意,如果 shebang 是#!/bin/sh
,即使 是/bin/sh
指向 bash 的链接,以下脚本片段也会起作用。但是,如果 shebang 是#!/bin/bash
,则需要添加shopt -s expand_aliases
命令以使别名在脚本中起作用。)
那么,我的第一个技巧是:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
现在,当我们说 时echo "Message"
,我们正在调用别名,它不会被跟踪。别名关闭跟踪选项,同时抑制来自命令的跟踪消息set
(使用首先在user5071535 的回答),然后执行实际echo
命令。这样我们就可以得到类似于 user5071535 的答案的效果,而无需编辑代码每一个 echo
命令。但是,这会使跟踪模式处于关闭状态。我们无法将 放入set -x
别名中(至少不容易),因为别名只允许用字符串替换单词;别名字符串的任何部分都不能注入命令中
后参数(例如"Message"
)。例如,如果脚本包含
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
输出将是
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
因此,您仍然需要在显示消息后重新打开跟踪选项 - 但每次显示消息后只需打开一次堵塞连续echo
命令:
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
那就太好了如果我们可以让set -x
之后的 自动化echo
— — 我们可以做到,但需要一些技巧。但在我介绍这一点之前,请考虑一下这一点。OP 从使用 shebang 的脚本开始#!/bin/sh -ex
。隐式地,用户可以x
从 shebang 中删除 并拥有一个正常工作的脚本,而无需执行跟踪。如果我们可以开发一种保留该属性的解决方案,那就太好了。这里的前几个答案都不符合该属性,因为它们在echo
语句之后无条件地“重新”打开跟踪,而不考虑它是否已经打开。
这个答案显然没有认识到这个问题,因为它取代 echo
使用跟踪输出输出;因此,如果关闭跟踪,所有消息都会消失。现在我将介绍一种解决方案,在语句之后重新打开echo
跟踪有条件的— 仅当它已经打开时才有效。将其降级为无条件开启“回溯”的解决方案很简单,留作练习。
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
是选项列表;对应于所有设置的选项的字母串联。例如,如果设置了e
和x
选项,那么将是包含和的$-
字母的混乱。我的新别名(上面)在关闭跟踪之前保存了的值。然后,在关闭跟踪的情况下,它将控制权交给 shell 函数。该函数执行实际操作 ,然后检查调用别名时选项是否已打开。如果选项已打开,则该函数将其重新打开;如果选项已关闭,则该函数将其保持关闭状态。e
x
$-
echo
x
shopt
您可以在脚本的开头插入上述七行(如果包含,则为八行),其余部分保持不变。
这将允许你
- 使用下列任一 shebang 行:
/bin/sh -ex #!/bin/sh -ex /bin/sh -e #!/bin/sh -e /bin/sh –x
或者只是/bin/sh #!/bin/sh 复制代码
它应该可以按预期工作。 - 有类似的代码
(她嘞) 命令1 命令2 命令3 设置-x 命令4 命令5 命令6 设置 +x 命令7 命令8 命令9
和- 命令 4、5 和 6 将被跟踪 — 除非其中一个是
echo
,在这种情况下它将被执行但不会被跟踪。(但即使命令 5 是echo
,命令 6 仍将被跟踪。) - 命令 7、8 和 9 将不会被跟踪。即使命令 8 是
echo
,命令 9 仍然不会被跟踪。 - 命令 1、2 和 3 将被跟踪(如 4、5 和 6)或不被跟踪(如 7、8 和 9),具体取决于 shebang 是否包含
x
。
- 命令 4、5 和 6 将被跟踪 — 除非其中一个是
PS 我发现,在我的系统上,我可以builtin
在中间答案中省略关键字(只是 的别名echo
)。这并不奇怪;bash(1) 表示,在别名扩展期间,…
…与正在扩展的别名相同的单词不会再次扩展。这意味着可以别名
ls
到ls -F
,例如,bash 不会尝试递归扩展替换文本。
不足为奇的是,echo_and_restore
如果builtin
省略关键字1 ,最后一个答案(带有 的答案)将失败。但是,奇怪的是,如果我删除builtin
并切换顺序,它就会起作用:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 它似乎会导致未定义的行为。我见过
- 无限循环(可能是由于无限制递归),
- 错误
/dev/null: Bad address
消息,以及 - 核心转储。
答案2
我找到了部分解决方案信息技术:
#!/bin/bash -ex
set +x;
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"
输出
set +x;
shell tracing is disabled here
+ echo "but is enabled here"
but is enabled here
不幸的是,回声仍然存在set +x
,但至少之后就安静了。所以这至少是问题的部分解决方案。
但有没有更好的方法来做到这一点?:)
答案3
通过摆脱输出,这种方式可以改进您自己的解决方案set +x
:
#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"
答案4
放在set +x
括号内,因此它仅适用于本地范围。
例如:
#!/bin/bash -x
exec 3<> /dev/null
(echo foo1 $(set +x)) 2>&3
($(set +x) echo foo2) 2>&3
( set +x; echo foo3 ) 2>&3
true
将输出:
$ ./foo.sh
+ exec
foo1
foo2
foo3
+ true