我有一个执行多个命令的 shell 脚本。如果其中任何一个命令以非零退出代码退出,我该如何使 shell 脚本退出?
我应该如何让 shell 脚本在命令出错时退出呢?$?
变量中找到退出码,因此您可能会看到以下内容:ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi
在处理管道命令时需要小心,因为$?
只会返回管道中最后一个元素的返回码。因此,在以下代码中:
ls -al file.ext | sed 's/^/xx: /"
sed
管道不会返回错误代码(因为管道中的sed
实际上是可以正常工作并返回0的)。在这种情况下,bash
shell提供了一个数组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
中。
ls -al file.ext || exit $?
([[ ]]
不是便携式的)。 - MarcH[[ ]]
在标记了bash
的问题中是相当通用的 :-) 奇怪的是,在command.com
中无法使用ls
,因此它也不具有可移植性,这种说法似乎有些牵强,但与你提出的论点相似。 - paxdiabloPIPESTATUS
获取管道中命令的退出代码(例如${PIPESTATUS[0]}
表示第一个命令的退出代码,${PIPESTATUS[1]}
表示第二个命令的退出代码,${PIPESTATUS[*]}
表示所有退出状态的列表)。 - DevSolar$?
。通常你想要像这样做:if ls -al file.ext; then : nothing; else exit $?; fi
,这当然等同于像@MarcH说的那样:ls -al file.ext || exit $?
,但如果then
或else
语句比较复杂,那么第一种写法更易于维护。 - tripleee[[ $rc != 0 ]]
会导致出现 0:未找到
或者 1:未找到
的错误。建议修改为 [ $rc -ne 0 ]
。同时,可以将 rc=$?
移除,直接使用 [ $? -ne 0 ]
。 - CurtisLeeBolin#!/bin/bash -e
是启动 shell 脚本的唯一方式。如果您需要实际检查退出状态,您可以始终使用诸如 foo || handle_error $?
的东西。 - Davis Herringset -e
是基于运行bash …/foo
并且失去了该选项吗?如果是这样,那么如果您选择从外部运行脚本,就有很多误运行脚本的方法... - Davis Herringset -e
。 - tripleeeset -e
" 是最简单的方法之一。只需在程序中的任何命令前添加它即可。set -e
命令会在你的脚本中任何一个命令以错误状态退出且你没有处理该错误时中止脚本。 - Andrew[ $? -eq 0 ] || exit $?; # Exit for nonzero return code
http://cfaj.freeshell.org/shell/cus-faq-2.html#11
11.如何获取在 cmd1|cmd2
中的 cmd1
的退出码?
首先需要注意的是,cmd1
的退出码可能是非零的并且仍不意味着出现了错误。例如,在以下情况下:
cmd | head -1
你可能会看到退出状态为 141(或 ksh93 下的 269)的 cmd1
,但这是因为当 head -1
在读取一行后终止时,cmd
被 SIGPIPE 信号中断。
若要知道管道元素的退出状态:
cmd1 | cmd2 | cmd3
a. 使用 Z shell (zsh
):
退出码被提供在 pipestatus 特殊数组中。
cmd1
的退出码在 $pipestatus[1]
,cmd3
的退出码在
$pipestatus[3]
,所以 $?
始终与 $pipestatus[-1]
相同。
b. 使用 Bash:
退出码被提供在 PIPESTATUS 特殊数组中。
cmd1
的退出码在 ${PIPESTATUS[0]}
,cmd3
的退出码在
${PIPESTATUS[2]}
,所以 $?
始终与 ${PIPESTATUS: -1}
相同。
...
更多详情请参见 Z shell。
对于 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
if (! command)
。 - Berci#
#------------------------------------------------------------------------------
# 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
}
$*
;相反,应该使用 "$@"
以保留空格和通配符。 - Davis Herring
$?
的值。简单方法:在Bash脚本的顶部放置set -e
或#!/bin/bash -e
。 - mwfearnley