Shell括号内未应用Shell选项更改(例如set -o xtrace)

时间:2018-02-02 00:46:39

标签: bash shell sh

我正在尝试为我的bashrc创建一个别名,如果未设置nounset,则启用选项xtracextrace,如果已设置,则禁用它们,允许我切换“调试模式”。

我有这个:

alias debug="[[ \$(set -o | grep xtrace) = *off ]] && (set -o nounset; set -o xtrace; echo 'Debug mode ON') || (set +o nounset; set +o xtrace; echo 'Debug mode OFF')"

我的问题是,这似乎可以查看终端输出,但是当我运行set -o时,nounsetxtrace选项都没有改变。原因是两个选项都在括号()内,因此选项不会应用于当前终端。

例如,这也不起作用(xtrace保持关闭状态):

(set -o xtrace)

我该如何解决这个问题?有没有办法在没有括号的情况下编写上面的别名,或者从括号内应用选项?为什么会这样?

2 个答案:

答案 0 :(得分:1)

$(…)(set -o ...;)中的括号创建子shell。在子shell中进行的更改对父shell没有影响,无论是环境变量还是shell选项。

您似乎想要大声切换跟踪标记。它也被称为-x标志,并且在$-中可见,该参数包含当前设置的单字母标志。您还可以使用set -uset +u作为set -o nounset选项。

因此,你可以使用一些变体:

alias debug='case "$-" in
             (*x*) set +xu; echo "Debug mode OFF";;
             (*)   set -xu; echo "Debug mode ON";;
             esac'

如上所述,如果您愿意,可以将其展平为一行。还有其他方法可以使用if和适当的条件来编写它。我仍然喜欢case,但在使用[[命令进行Bash之前我学习了shell脚本。

您还可以使用{}代替子shell表示法。因此,对脚本进行极简主义的更改将是:

alias debug="[[ \$(set -o | grep xtrace) = *off ]] && { set -o nounset; set -o xtrace; echo 'Debug mode ON'; } || { set +o nounset; set +o xtrace; echo 'Debug mode OFF'; }"

请注意,在}之前必须有一个“命令结束” - 实际上,这意味着换行符或分号。 {}必须与周围的符号分开。我不喜欢使用$(set -o | grep xtrace);与使用$-相比,这是非常沉重的。你可以再次使用$-来解决这个问题:

alias debug='[[ ! $- =~ x ]] && { set -o nounset -o xtrace; echo "Debug mode ON"; } || { set +o nounset +o xtrace; echo "Debug mode OFF"; }'

打开调试时有点吵;它最好写成:

alias debug='[[ ! $- =~ x ]] && { echo "Debug mode ON"; set -o nounset -o xtrace; } || { set +o nounset +o xtrace; echo "Debug mode OFF"; }'

在跟踪关闭时完成echo。当调试当前处于打开状态并且您即将关闭时,您仍然可以看到测试结果。这是不可避免的。

您还可以使用明确的if / then / else / fi

alias debug='if [[ $- =~ x ]]; then set +o nounset +o xtrace; echo "Debug mode OFF"; else echo "Debug mode ON"; set -o nounset -o xtrace; fi'

或者压缩到:

alias debug='if [[ $- =~ x ]]; then set +xu; echo "Debug mode OFF"; else echo "Debug mode ON"; set -xu; fi'

答案 1 :(得分:1)

alias debug="[[ \$(set -o | grep xtrace) = *off ]] && { set -o nounset; set -o xtrace; echo 'Debug mode ON'; } || { set +o nounset; set +o xtrace; echo 'Debug mode OFF'; }"

{ }可以将多个命令组合在一起,而无需像()那样创建子shell。语法有点不同;你需要确保在{ }和任何单词之间留下空格,否则它可以被解释为{a,b} → a b大括号扩展,并且命令需要以;(或{{ 1}})。