我有一个执行许多命令的 shell 脚本。如果任何命令以非零退出代码退出,我如何使 shell 脚本退出?
9 回答
在每个命令之后,可以在$?
变量中找到退出代码,因此您将拥有如下内容:
ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
您需要小心管道命令,因为它$?
只会为您提供管道中最后一个元素的返回码,因此,在代码中:
ls -al file.ext | sed 's/^/xx: /"
如果文件不存在,则不会返回错误代码(因为sed
管道的部分实际工作,返回 0)。
shell 实际上提供了一个可以在这种情况下提供帮助的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 -e
" 可能是最简单的方法。只需将其放在程序中的任何命令之前即可。
如果你只是在 Bash 中不带任何参数地调用 exit,它将返回最后一个命令的退出代码。结合OR
,如果前一个命令失败,Bash 应该只调用 exit。但我没有测试过这个。
命令1 || 出口; 命令2 || 出口;
Bash 还会将最后一个命令的退出代码存储在变量 中$?
。
[ $? -eq 0 ] || exit $?; # Exit for nonzero return code
http://cfaj.freeshell.org/shell/cus-faq-2.html#11
如何获得
cmd1
in的退出代码cmd1|cmd2
首先,请注意
cmd1
退出代码可能不为零,但并不意味着错误。这发生在例如cmd | head -1
您可能会观察到 141(或带有 ksh93 的 269)退出状态
cmd1
,但这是因为在读取一行后终止cmd
时被 SIGPIPE 信号中断。head -1
了解管道元素的退出状态
cmd1 | cmd2 | cmd3
一种。使用Z 壳(
zsh
):退出代码在 pipestatus 特殊数组中提供。
cmd1
退出代码在$pipestatus[1]
,cmd3
退出代码在$pipestatus[3]
,所以$?
总是与$pipestatus[-1]
.湾。与重击:
退出代码在
PIPESTATUS
特殊数组中提供。cmd1
退出代码在${PIPESTATUS[0]}
,cmd3
退出代码在${PIPESTATUS[2]}
,所以$?
总是与${PIPESTATUS: -1}
....
有关更多详细信息,请参阅Z shell。
对于重击:
# 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
#
#------------------------------------------------------------------------------
# purpose: to run a command, log cmd output, exit on error
# usage:
# set -e; do_run_cmd_or_exit "$cmd" ; set +e
#------------------------------------------------------------------------------
do_run_cmd_or_exit(){
cmd="$@" ;
do_log "DEBUG running cmd or exit: \"$cmd\""
msg=$($cmd 2>&1)
export exit_code=$?
# If occurred during the execution, exit with error
error_msg="Failed to run the command:
\"$cmd\" with the output:
\"$msg\" !!!"
if [ $exit_code -ne 0 ] ; then
do_log "ERROR $msg"
do_log "FATAL $msg"
do_exit "$exit_code" "$error_msg"
else
# If no errors occurred, just log the message
do_log "DEBUG : cmdoutput : \"$msg\""
fi
}