暂时取消设置 bash 选项 -x

暂时取消设置 bash 选项 -x

我喜欢在脚本中使用来set -x显示正在发生的情况,特别是如果脚本将在 CI/CD 管道中运行并且我可能需要事后调试一些故障。

这样做的一个烦恼是,如果我想向用户回显一些文本(例如,状态消息或"I'm starting to do $X"),那么该消息会输出两次 - 一次用于echo回显命令本身,然后一次作为该echo命令的输出。

有什么好方法可以让这个变得更好?一种解决方案是这样的:

set -x

... bunch of normal commands that get echoed

(
  # Temporarily don't echo, so we don't double-echo
  set +x
  echo "Here is my status message"
)

... rest of commands get echoed again

但这样做的两个问题是

  1. 每次我想告诉用户一些事情时,都要写很多东西,而且它足够“不明显”,以至于每次都可能需要注释
  2. set +x也呼应了,这是不可取的。

还有其他效果好的选择吗?

就像是Make 的功能是在前面添加 an@来抑制回声那就太好了,但我还没能在 Bash 中找到这样的功能。

答案1

您可以通过以下方式在 Bash 中实现此目的不是使用set -x而不是捕获DEBUG并进行自己的跟踪:

#!/bin/bash

set -T

trap '! [[ "$BASH_COMMAND" =~ ^(echo|printf) ]] &&
      printf "+ %s\n" "$BASH_COMMAND"' DEBUG

foo=bar
echo This is a test
echo $foo
[[ $foo = bar ]] && /usr/bin/printf 'Matched\n'

这个想法是将您想要忽略的命令添加到该行的正则表达式中trap。运行上面的命令会产生

+ foo=bar
This is a test
bar
+ [[ $foo = bar ]]
+ /usr/bin/printf 'Matched\n'
Matched

set -T确保陷阱由 shell 函数继承。

您可以添加单独的机制来启用和禁用陷阱,例如使用 shell 变量:

#!/bin/bash

set -T

status() {
        NOTRACE=1 echo "$@"
}

trap '! [[ "$BASH_COMMAND" =~ ^(status|NOTRACE=) ]] &&
      printf "+ %s\n" "$BASH_COMMAND"' DEBUG

foo=bar
test=$(echo baz)
echo This is a test
status This is a status report
echo $foo
[[ $foo = bar ]] && /usr/bin/printf 'Matched\n'

这会产生

+ foo=bar
+ test=$(echo baz)
+ echo This is a test
This is a test
This is a status report
+ echo $foo
bar
+ [[ $foo = bar ]]
+ /usr/bin/printf 'Matched\n'
Matched

答案2

这是一个可怕的克鲁格,我觉得建议它很肮脏,但是......你可以用魔法别名。这个技巧的关键是别名作为命令执行的解析阶段的一部分进行扩展,因此set -x在扩展时不会打印任何内容(与函数不同)。因此,您可以创建一个别名,在命令之前添加“关闭-x”样板文件echo,然后发现您还需要一个函数来-x在最后运行“重新打开”样板文件。

您还需要在脚本中打开别名扩展。它通常是禁用的,因此,例如,如果您有类似grep别名的内容grep --color,那么每当脚本使用 时,就不会随机注入颜色代码grep。因此,最安全的做法是unalias -a先运行,删除任何可能带来麻烦的别名。

无论如何,这是代码:

unalias -a
shopt -s expand_aliases
alias cleanecho='{ set +x; } 2>/dev/null; resetx_after echo'
resetx_after() { "$@"; set -x; }

set -x
cleanecho "Ha, ha, you can't see the command that printed this!"

它是如何工作的:类似的命令cleanecho "something"扩展为:

{ set +x; } 2>/dev/null; resetx_after echo "something"

{ set +x; } 2>/dev/null关闭-x模式(将其自己的跟踪重定向到/dev/null)。然后resetx_after echo "something"运行,执行:

{ echo "something"; set -x; }

...打印字符串然后-x重新打开跟踪。

顺便说一句,如果您希望能够printf类似地使用其他命令,您可以为它们添加类似的别名:

alias cleanprintf='{ set +x; } 2>/dev/null; resetx_after printf'

...或者只是创建一个通用的 don-trace-this 别名来用作前缀:

alias notrace='{ set +x; } 2>/dev/null; resetx_after'
notrace printf 'set -x disabled for this command\n'

答案3

简单的。你已经解决了一半:只要搬进set -x街区

#!/bin/sh
echo a
(
    set -x
    echo b
)
echo c
$ a.sh
a
+ echo b
b
c

如果您不想跟踪脚本的一部分 - 分两部分进行跟踪

#!/bin/sh
echo a
(
    set -x
    echo b
)
echo c
(
    set -x
    echo d
)
echo e

只需追踪可疑的片段而不是整个脚本。

相关内容