抑制 echo 命令的执行跟踪吗?

抑制 echo 命令的执行跟踪吗?

我正在运行 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
}

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

shopt您可以在脚本的开头插入上述七行(如果包含,则为八行),其余部分保持不变。

这将允许你

  1. 使用下列任一 shebang 行:
    /bin/sh -ex #!/bin/sh -ex
    /bin/sh -e #!/bin/sh -e
    /bin/sh –x
    或者只是
    /bin/sh #!/bin/sh 复制代码
    它应该可以按预期工作。
  2. 有类似的代码
    (她嘞)
    命令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

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'

__________
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

相关内容