dvd*_*sng 31 scripting bash parameter shell-script trap
在使用set -e
( errexit
), set -u
( nounset
) 以及 ERR 和 EXIT 陷阱时,我观察到一些奇怪的行为。它们似乎相关,因此将它们放在一个问题中似乎是合理的。
set -u
不触发 ERR 陷阱代码:
#!/bin/bash
trap 'echo "ERR (rc: $?)"' ERR
set -u
echo ${UNSET_VAR}
Run Code Online (Sandbox Code Playgroud)set -e
不会改变结果set -eu
在 EXIT 陷阱中使用退出代码是 0 而不是 1代码:
#!/bin/bash
trap 'echo "EXIT (rc: $?)"' EXIT
set -eu
echo ${UNSET_VAR}
Run Code Online (Sandbox Code Playgroud)set +e
,RC == 1。当任何其他命令抛出错误时,EXIT 陷阱返回正确的 RC。任何人都可以解释这些行为吗?
搜索这些主题并不是很成功,考虑到有关 Bash 设置和陷阱的帖子数量,这相当令人惊讶。虽然有一个论坛帖子,但结论并不令人满意。
mik*_*erv 18
来自man bash
:
set -u
"@"
和"*"
执行参数扩展时为错误。如果在未设置的变量或参数上尝试扩展,shell 会打印一条错误消息,如果不是-i
交互式的,则以非零状态退出。POSIX 指出,在发生扩展错误的情况下,当扩展与 shell 特殊内置(这是一个经常忽略的区别,因此可能无关紧要)或除此之外的任何其他实用程序关联时,非交互式 shell应退出.bash
"${x!y}"
因为!
不是有效的操作者) ; 如果能够在标记化期间而不是在扩展期间检测到它们,则实现可以将它们视为语法错误。也来自man bash
:
trap ... ERR
while
或until
关键字...if
声明中测试的一部分...&&
或||
在最终除命令列表&&
或||
...!
.-e
选项所遵循的相同条件。注意上面的ERR陷阱是关于评估其他命令的返回。但是当发生扩展错误时,没有命令 run 返回任何内容。在您的示例中,echo
永远不会发生- 因为当 shell 评估和扩展其参数时,它遇到了一个-u
nset 变量,该变量已由显式 shell 选项指定,以导致立即退出当前的脚本 shell。
因此EXIT陷阱(如果有)被执行,并且 shell 以诊断消息和非 0 的退出状态退出——正如它应该做的那样。
至于rc: 0 的事情,我希望这是某种版本特定的错误 - 可能与EXIT的两个触发器同时发生和一个获取另一个退出代码(不应该发生)有关。无论如何,bash
安装了最新的二进制文件pacman
:
bash <<\IN
printf "shell options:\t$-\n"
trap 'echo "EXIT (rc: $?)"' EXIT
set -eu
echo ${UNSET_VAR}
IN
Run Code Online (Sandbox Code Playgroud)
我添加了第一行,以便您可以看到 shell 的条件是脚本 shell 的条件 - 它不是交互式的。输出是:
shell options: hB
bash: line 4: UNSET_VAR: unbound variable
EXIT (rc: 1)
Run Code Online (Sandbox Code Playgroud)
以下是最近更新日志中的一些相关说明:
$?
正确的错误。for
的命令有错误的行号。trap
在异步子 shell 命令中不可用的错误。trap
为这些信号运行处理程序时,外壳不再阻止接收信号,并允许大多数 trap
处理程序递归
运行(在trap
处理trap
程序执行时运行处理程序)。我认为最相关的要么是最后一个,要么是第一个——或者可能是两者的结合。一个trap
处理程序是其本质异步的,因为它的整个工作就是等待和处理异步信号。你用-eu
和同时触发两个$UNSET_VAR
。
所以也许你应该只更新,但如果你喜欢自己,你会用一个完全不同的 shell 来完成。
meu*_*euh 12
(我使用的是 bash 4.2.53)。对于第 1 部分,bash 手册页只是说“错误消息将写入标准错误,并且非交互式 shell 将退出”。它并没有说会调用 ERR 陷阱,但我同意如果调用它会很有用。
务实地说,如果您真正想要更干净地处理未定义的变量,一个可能的解决方案是将大部分代码放在一个函数中,然后在子 shell 中执行该函数并恢复返回代码和 stderr 输出。这是一个示例,其中“cmd()”是函数:
#!/bin/bash
trap 'rc=$?; echo "ERR at line ${LINENO} (rc: $rc)"; exit $rc' ERR
trap 'rc=$?; echo "EXIT (rc: $rc)"; exit $rc' EXIT
set -u
set -E # export trap to functions
cmd(){
echo "args=$*"
echo ${UNSET_VAR}
echo hello
}
oops(){
rc=$?
echo "$@"
return $rc # provoke ERR trap
}
exec 3>&1 # copy stdin to use in $()
if output=$(cmd "$@" 2>&1 >&3) # collect stderr, not stdout
then echo ok
else oops "fail: $output"
fi
Run Code Online (Sandbox Code Playgroud)
在我的 bash 我得到
./script my stuff; echo "exit was $?"
args=my stuff
fail: ./script: line 9: UNSET_VAR: unbound variable
ERR at line 15 (rc: 1)
EXIT (rc: 1)
exit was 1
Run Code Online (Sandbox Code Playgroud)