抑制 echo 命令的执行跟踪?

Chr*_*ian 36 bash shell command-line bash-scripting jenkins

我正在运行 Jenkins 的 shell 脚本,它使用 shebang options 启动 shell 脚本#!/bin/sh -ex

根据Bash Shebang 的假人?, -x, “导致 shell 打印执行跟踪”,这对于大多数用途都非常有用——除了回声:

echo "Message"
Run Code Online (Sandbox Code Playgroud)

产生输出

+ echo "Message"
Message
Run Code Online (Sandbox Code Playgroud)

这有点多余,看起来有点奇怪。有没有办法保持-x启用,但只输出

Message
Run Code Online (Sandbox Code Playgroud)

而不是上面的两行,例如通过在 echo 命令前加上一个特殊的命令字符,或者重定向输出?

G-M*_*ca' 33

当您在短吻鳄的脖子上时,很容易忘记目标是排干沼泽。?????????????????? ——流行的说法

问题是关于echo,但到目前为止,大多数答案都集中在如何潜入set?+x命令上。有一个更简单、更直接的解决方案:

{ echo "Message"; } 2> /dev/null
Run Code Online (Sandbox Code Playgroud)

(我承认,{ …; } 2> /dev/null 如果我没有在之前的答案中看到它,我可能不会想到它。)

这有点麻烦,但是,如果您有一组连续的echo命令,则无需对每个命令单独执行:

{
  echo "The quick brown fox"
  echo "jumps over the lazy dog."
} 2> /dev/null
Run Code Online (Sandbox Code Playgroud)

请注意,有换行符时不需要分号。

您可以减少使用打字负担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
Run Code Online (Sandbox Code Playgroud)

(请注意,如果 shebang 是#!/bin/sh,即使/bin/sh是指向 bash 的链接,以下脚本片段也有效。但是,如果 shebang 是#!/bin/bash,则需要添加一个shopt -s expand_aliases命令以使别名在脚本中工作。)

所以,对于我的第一个技巧:

alias echo='{ set +x; } 2> /dev/null; builtin echo'
Run Code Online (Sandbox Code Playgroud)

现在,当我们说 时echo "Message",我们正在调用别名,它不会被跟踪。别名关闭跟踪选项,同时抑制来自set命令的跟踪消息(使用在user5071535 的回答中首先介绍的技术),然后执行实际echo命令。这让我们获得了类似于 user5071535 的答案的效果,而无需在每个 echo命令中编辑代码。但是,这会导致跟踪模式关闭。我们不能将 aset?-x放入别名中(或者至少不容易),因为别名只允许用字符串替换单词;别名字符串的任何部分都不能在参数之后注入命令中 (例如,"Message")。因此,例如,如果脚本包含

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
Run Code Online (Sandbox Code Playgroud)

输出将是

+ 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
Run Code Online (Sandbox Code Playgroud)

所以你仍然需要在显示消息后重新打开跟踪选项 - 但在每个连续命令之后只需要一次echo

date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Run Code Online (Sandbox Code Playgroud)


如果我们能在 aset?-x之后自动制作就好echo——而且我们可以,但需要更多技巧。但在我介绍之前,请考虑一下。OP 从使用#!/bin/sh?-exshebang 的脚本开始。隐式地,用户可以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
}
Run Code Online (Sandbox Code Playgroud)

$-是选项列表;与设置的所有选项对应的字母的串联。例如,如果设置了ex选项,$-则将是包含e和的一堆字母x。我的新别名(上图)$-在关闭跟踪之前保存了 的值。然后,在关闭跟踪的情况下,它将控制权交给 shell 函数。该函数执行实际操作echo ,然后检查x在调用别名时是否打开了该选项。如果该选项打开,则该功能将其重新打开;如果它已关闭,则该功能将其关闭。

您可以shopt在脚本的开头插入以上七行(八行,如果包含),其余部分不理会。

这会让你

  1. 使用以下任何shebang行:
    #!/bin/sh -ex
    #!/bin/sh -e
    #!/bin/sh –x
    或者只是简单的
    #!/bin/sh
    它应该按预期工作。
  2. 有类似的代码
    (shebang)
    命令1
    命令2
    命令3
    设置 -x
    命令4
    命令5
    命令6
    设置 +x
    命令7
    命令8
    命令9
    • 命令 4、5 和 6 将被跟踪 - 除非其中一个是 an echo,在这种情况下它将被执行但不会被跟踪。(但即使命令 5 是echo,命令 6 仍然会被跟踪。)
    • 命令 7、8 和 9 将不会被跟踪。即使命令 8 是echo,命令 9 仍然不会被跟踪。
    • 命令 1、2 和 3 将被追踪(如 4、5 和 6)或不追踪(如 7、8 和 9),具体取决于shebang 是否包含x.

PS 我发现,在我的系统上,我可以builtin在我的中间答案中省略关键字(那个只是 的别名echo)。这并不奇怪;bash(1) 表示,在别名扩展期间,...

... 与被扩展的别名相同的单词不会再次扩展。这意味着一个可能的别名lsls?-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'
Run Code Online (Sandbox Code Playgroud)

__________
1 ?它似乎会引起未定义的行为。我见过

  • 无限循环(可能是因为无限递归),
  • 一个/dev/null: Bad address错误消息,并且
  • 核心转储。

  • 我见过一些用别名完成的神奇魔术,所以我知道我的知识是不完整的。如果有人可以提出一种方法来做相当于```echo +x; 回声“$*”;echo -x``` 在别名中,我想看一下。 (2认同)

Chr*_*ian 15

我在InformIT找到了部分解决方案:

#!/bin/bash -ex
set +x; 
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"
Run Code Online (Sandbox Code Playgroud)

产出

set +x; 
shell tracing is disabled here 
+ echo "but is enabled here"
but is enabled here
Run Code Online (Sandbox Code Playgroud)

不幸的是,这仍然是回声set +x,但至少在那之后它很安静。所以它至少是问题的部分解决方案。

但也许有更好的方法来做到这一点?:)


小智 5

这种方式通过摆脱set +x输出来改进您自己的解决方案:

#!/bin/bash -ex
{ set +x; } 2>/dev/null
echo "shell tracing is disabled here"; set -x;
echo "but is enabled here"
Run Code Online (Sandbox Code Playgroud)