我已经阅读了各种堆栈交换站点和 unix 帮助论坛上有关如何修改 shell 选项然后恢复它们的许多问题 - 我在这里找到的最全面的问题是如何“撤消”`set -x`?
这获得智慧似乎是保存set +o
或shopt -po
然后eval
稍后恢复以前的设置的结果。
但是,在我自己使用 bash 3.x 和 4.x 进行的测试中,errexit
执行命令替换时该选项未正确保存。
这是一个显示该问题的示例脚本:
set -o errexit
set -o nounset
echo "LOCAL SETTINGS:"
set +o
OLDOPTS=$(set +o)
echo
echo "SAVED SETTINGS:"
echo "$OLDOPTS"
和输出(我删除了一些不相关的变量):
LOCAL SETTINGS:
set -o errexit
set -o nounset
SAVED SETTINGS:
set +o errexit
set -o nounset
这看起来极其危险。我编写的大多数脚本都依赖于errexit
在任何命令失败时停止执行。我刚刚在我的一个脚本中发现了由此引起的一个错误,其中应该在最后恢复的函数errexit
最终覆盖了它,将其设置回默认值离开在脚本的持续时间内。
我想要做的是编写可以根据需要设置选项然后恢复的函数全部退出前正确选择选项。但似乎在命令替换调用的子shell中,errexit
是不能继承的。
我不知道如何在set +o
不使用命令替换或跳过 FIFO 环的情况下保存结果。我可以读取,$SHELLOPTS
但它不可写或无法采用eval
-able 格式。
我知道一种替代方法是使用子shell函数,但这给记录输出以及传回多个变量带来了很多麻烦。
大概相关:https://stackoverflow.com/questions/29532904/bash-subshell-errexit-semantics(似乎 bash 4.4 及更高版本有一个解决方法,但我宁愿有一个便携式解决方案)
答案1
你正在做的事情应该有效。但 bash 在命令替换中关闭了该errexit
选项,因此它保留了除此之外的所有选项。这是特定于 bash 和特定于errexit
选项的。 Basherrexit
在 POSIX 模式下运行时确实会保留。从 bash 4.4 开始,bash 也不会errexit
在命令替换中清除是否shopt -s inherit_errexit
有效。
由于该选项在命令替换内运行任何代码之前已关闭,因此您必须在外部检查它。
OLDOPTS=$(set +o)
case $- in
*e*) OLDOPTS="$OLDOPTS; set -e";;
*) OLDOPTS="$OLDOPTS; set +e";;
esac
如果您不喜欢这种复杂性,请改用 zsh。
setopt local_options
答案2
在 alpine 3.6 上尝试了上述旧方法之后,我现在采用了以下更简单的方法:
OLDOPTS="$(set +o); set -${-//c}"
set -euf -o pipefail
... my stuff
# restore original options
set +vx; eval "${OLDOPTS}"
根据文档,“$-”保存当前活动选项的列表。看起来效果很好,我错过了什么吗?
答案3
errexit
传播到进程替换中。
set -e
# Backup restore commands into an array
declare -a OPTS
readarray -t OPTS < <(shopt -po)
set +e
# Restore options
declare cmd
for cmd in "${OPTS[@]}"; do
eval "$cmd"
done
查看:
$ shopt -po errexit
set -o errexit
重击版本:
$ bash --version
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
答案4
简单的解决方案是将errexit
设置附加到OLDOPTS
:
OLDOPTS="$(set +o)"
[ "${BASH_VERSION:+x}" ] && shopt -qo errexit && OLDOPTS+=";set -e" || true
完毕。