我有一个执行许多命令的shell脚本.如果任何命令以非零退出代码退出,如何使shell脚本退出?
在每个命令之后,退出代码可以在$?
变量中找到,所以你会有类似的东西:
ls -al file.ext rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
你需要注意管道命令,因为$?
它只给你管道中最后一个元素的返回码,所以在代码中:
ls -al file.ext | sed 's/^/xx: /"
如果文件不存在,则不会返回错误代码(因为sed
管道的一部分实际工作,返回0).
该bash
壳实际上提供一种阵列,其可以协助在这种情况下,即是PIPESTATUS
.此数组为每个管道组件都有一个元素,您可以单独访问,如${PIPESTATUS[0]}
:
pax> false | true ; echo ${PIPESTATUS[0]} 1
请注意,这是获取false
命令的结果,而不是整个管道.您还可以根据需要获取整个列表进行处理:
pax> false | true | false; echo ${PIPESTATUS[*]} 1 0 1
如果您想从管道中获取最大的错误代码,可以使用以下内容:
true | true | false | true | false rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done echo $rc
这PIPESTATUS
依次遍历每个元素,rc
如果它大于前一个rc
值,则将其存储.
如果你想使用$ ?,你需要在每个命令之后检查它,因为$?每个命令退出后更新.这意味着如果执行管道,您将只获得管道中最后一个进程的退出代码.
另一种方法是这样做:
set -e set -o pipefail
如果你把它放在shell脚本的顶部,看起来bash会为你处理这个问题.正如之前的一张海报所指出的那样,"set -e"会导致bash在任何简单命令上都出错."set -o pipefail"将导致bash退出,同时管道中的任何命令都会出错.
请参阅此处或此处以获取有关此问题的更多讨论. 这是set builtin上的bash手册部分.
" set -e
"可能是最简单的方法.只需将它放在程序中的任何命令之前.
如果你只是在没有参数的bash中调用exit,它将返回最后一个命令的退出代码.结合OR,如果前一个命令失败,bash应该只调用exit.但我没有测试过这个.
command1 || exit; command2 || exit;
Bash还会将最后一个命令的退出代码存储在变量$?中.
[ $? -eq 0 ] || exit $?; # exit for none-zero return code
http://cfaj.freeshell.org/shell/cus-faq-2.html#11
我如何获得的退出代码cmd1
中cmd1|cmd2
首先,请注意cmd1
退出代码可能不为零,但仍然不代表错误.例如,这发生在
cmd | head -1
您可能会观察到141(或269与ksh93)退出状态cmd1
,但这是因为在读取一行后终止cmd
时被SIGPIPE信号中断
head -1
.
要知道管道元素的退出状态
cmd1 | cmd2 | cmd3
一个.用zsh:
退出代码在pipestatus特殊数组中提供.
cmd1
退出代码在$pipestatus[1]
,cmd3
退出代码
$pipestatus[3]
,因此$?
始终相同
$pipestatus[-1]
.
湾 用bash:
退出代码在PIPESTATUS
特殊数组中提供.
cmd1
退出代码在${PIPESTATUS[0]}
,cmd3
退出代码
${PIPESTATUS[2]}
,因此$?
始终相同
${PIPESTATUS: -1}
.
...
有关详细信息,请参阅以下链接.
对于bash:
# this will trap any errors or commands with non-zero exit status # by calling function catch_errors() trap catch_errors ERR; # # ... the rest of the script goes here # function catch_errors() { # do whatever on errors # # echo "script aborted, because of errors"; exit 0; }
在bash中这很容易,只需用&&将它们绑在一起:
command1 && command2 && command3
您还可以使用嵌套的if结构:
if command1 then if command2 then do_something else exit fi else exit fi