我有一个函数,想使用pipefail
里面的选项。但我不想简单地这样做set -o pipefail
,因为我担心脚本的另一部分可能不会pipefail
被设置。当然我可以set +o pipefail
稍后做,但如果pipefail
设置在我的函数之外,这也可能会引入意外的行为。
当然,如果设置了,我可以使用退出代码false | true
作为衡量标准pipefail
,但这似乎有点脏。
是否有更通用(也许规范?)的方法来检查设置的 bash 选项?
答案1
在bash-4.4
以上内容中,您可以使用:
myfunction() {
local -
set -o someoption
...
}
一些注意事项:
- 这是从 复制的
ash
。在 中ash
,想法是使$-
(存储选项集)本地化。它起作用是ash
因为 inash
,local
不会影响变量的值或属性(与bash
它最初取消设置的位置相反)。尽管如此,它也以与特殊情况bash
相同的方式工作ash
,也就是说,它不会导致$-
被取消设置或恢复为默认行为 - 它仅涵盖使用 设定的选项
set -o option
,而不涵盖使用 设定的选项shopt -s option
(bash
是唯一具有两组选项的 shell!) 与局部变量一样,它是动态作用域,而不是静态的。
$-
从函数返回时,的值将被恢复,但新设置将影响函数调用的其他函数或源脚本。如果您想要静态作用域,则需要切换到 ksh93 并使用:function myfunction { set -o myoption ... other_function }
other_function
只要它function other_function {
也是用语法(而不是 Bourneother_function()...
语法)声明的,就不受影响。由于它不会重置选项,因此您的函数可能仍会受到其他代码设置的选项的影响。为了避免这种情况,您需要使用
zsh
它。zsh
相当于local -
isset -o localoptions
,但你也可以得到一个众所周知的仿真模式本地。例如:myfunction() { emulate -L zsh set -o someoption ... }
将以一组合理的 zsh-default 选项开始(有一些例外,请参阅文档),在其上添加您的选项。这也是像 ash/bash 中那样的动态作用域,但您也可以使用以下方式调用其他函数:
emulate zsh -c 'other_function...'
other_function
以便使用普通 zsh 选项集来调用它。zsh 还有许多运算符可以让您避免首先全局更改选项(例如
(N)
/用于/ 的glob(D)
限定符,用于不区分大小写的通配符的 glob 运算符,以避免与,混合)nullglob
dotglob
(#i)
${(s:x:)var}
$IFS
${~var}
要求在参数扩展时进行通配(您需要noglob
在其他类似 Bourne 的 shell 中避免(!) 它)...
答案2
该$SHELLOPTS
变量保存所有用冒号分隔的设置选项。例如,它可能看起来像:
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
要检查给定选项,可以执行以下操作:
# check if pipefail is set, if not set it and remember this
if [[ ! "$SHELLOPTS" =~ "pipefail" ]]; then
set -o pipefail;
PIPEFAIL=0;
else
PIPEFAIL=1;
fi
# do something here
# reset pipefail, if it was not set before
if [ "$PIPEFAIL" -eq 0 ]; then
set +o pipefail;
fi
答案3
如果您想在函数内设置 shell 选项,但又不想影响调用者,可以通过两种不同的方式进行:
选项 1:子 shell
将函数放入子 shell 中(请注意函数语句周围的括号而不是大括号):
function foo() (
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
)
然后,调用者可以取消设置 pipelinefail 并且不受影响:
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
或者
选项 2:测试和重置
您可以根据需要测试并重置该选项(请注意函数语句周围的花括号——没有子 shell):
function foo() {
shopt -q -o pipefail
local resetpipefail=$?
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
# only reset pipefail if needed
[[ $resetpipefail -eq 1 ]] && set +o pipefail
}
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
帽子提示库恩勒姆他们的shopt -q
回答。
答案4
使用 RETURN 陷阱
使用陷阱指定的命令在使用或返回
RETURN
执行的 shell 函数或 shell 脚本恢复之前执行。.
source
[...]
选项-p
[to shopt] 使输出以可重复用作输入的形式显示。
foo() {
trap "$(shopt -p -o pipefail)" RETURN
set -o pipefail
echo inside foo
shopt -o pipefail
# ...
}
测试
$ shopt -o pipefail
pipefail off
$ foo
inside foo
pipefail on
$ shopt -o pipefail
pipefail off
以下函数模板处理一般情况,确保函数返回时set
和shopt
选项都恢复为其原始值:
foo() {
trap "$(shopt -p -o; shopt -p)" RETURN # ①
# ...
}
①shopt -p -o
可简化为标准等效set +o
。