如何保存和还原所有外壳程序选项,包括errexit


9

我已经在各种堆栈交换站点和UNIX帮助论坛上通读了许多有关如何修改Shell选项然后恢复它们的问题-我在这里找到的最全面的解决方法是如何“撤消” set -x`?

收到的智慧似乎是要么保存过的结果set +oshopt -poeval后来恢复到以前的设置。

但是,在我自己使用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在最后恢复,但此功能将覆盖它,并在脚本持续时间内将其设置为默认关闭状态

我想做的是编写可以根据需要设置选项的函数,然后在退出之前正确还原所有选项。但是,似乎在命令替换调用的子外壳中,errexit是不会被继承的。

我不知道如何保存结果set +o而不使用命令替换或跳过FIFO箍。我可以阅读,$SHELLOPTS但不是可写或eval-able格式。

我知道一种替代方法是使用subshel​​l函数,但是这给能够记录输出以及传递多个变量带来了很多麻烦。

可能相关:https : //stackoverflow.com/questions/29532904/bash-subshel​​l-errexit-semantics(似乎有一个针对bash 4.4及更高版本的解决方法,但我宁愿有一个便携式解决方案)


这不会还原bash shopt设置选项(如nullglob)。
艾萨克(Isaac)

Answers:


6

您正在做的事情应该可以工作。但是bash会关闭errexit命令替换中的选项,因此它将保留除此选项以外的所有选项。这特定于bash和特定于errexit选项。errexit当在POSIX模式下运行时,Bash会保留下来。从bash 4.4开始,errexit如果shopt -s inherit_errexit有效,bash也不会在命令替换中清除。

由于该选项在命令替换内运行任何代码之前已关闭,因此您必须在外部进行检查。

OLDOPTS=$(set +o)
case $- in
  *e*) OLDOPTS="$OLDOPTS; set -e";;
  *) OLDOPTS="$OLDOPTS; set +e";;
esac

如果您不喜欢这种复杂性,请改用zsh。

setopt local_options

3
请注意,bash4.4现在有local -(单ash)作为一个相当于zshsetopt localoptions(或ksh88默认情况下做什么)
斯特凡Chazelas

另请参阅shopt -s lastpipe; set +o | IFS= read -rd '' OLDOPTS || :(两组选项是bash的特质)。
斯特凡Chazelas

更简单:OLDOPTS="$(set +o); set -$-"
艾萨克

2

在alpine 3.6上尝试上述方法之后,我现在采用了以下简单得多的方法:

OLDOPTS="$(set +o); set -${-//c}"
set -euf -o pipefail

... my stuff

# restore original options
set +vx; eval "${OLDOPTS}"

根据文档,“ $-”保存当前活动选项的列表。看起来工作不错,我有什么想念的吗?


@Isaac我使用'set + euf',因为$-仅包含活动选项的列表。即,在重置时,我首先停用所有内容,然后仅重新激活以前处于活动状态的选项列表(我选择“ euf”,因为这些是我通常会修改的唯一选项-对于一般用途,它可能应包含所有可能选项的列表)。关于'set + vx'的要点,我相应地修改了上面的示例
Erich Eichinger

可能适用于errexit,但不适用于边界检查或遍历(set -uf
Erich Eichinger

@以撒我站得住了。我不知道我做错了什么。多谢您的协助!也很高兴看到您遇到并解决了-c标志问题-我希望我早些时候已经看到您的评论了;)
Erich Eichinger

我根据@Isaac的反馈更新了上述解决方案
Erich Eichinger

1

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)

1

简单的解决方案是将errexit设置附加到OLDOPTS

OLDOPTS="$(set +o)"
[ "${BASH_VERSION:+x}" ] && shopt -qo errexit && OLDOPTS+=";set -e" || true

做完了


另请参见[[ -o errexit ]](KSH / bash中/ zsh中)和[ -o errexit ](庆典/ KSH /佳日)
斯特凡Chazelas

shopt是bash的有效命令,没有有意义的理由可以避免(我只是您个人喜好如何编写答案的问题)。没有意义的变化。@StéphaneChazelas–
以撒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.