it-swarm.cn

基于进程退出代码退出Shell脚本

我有一个Shell脚本执行许多命令。如果任何命令以非零退出代码退出,如何使Shell脚本退出?

360
Mark Roddy

在每个命令之后,退出代码可以在$?变量中找到,所以你会得到类似的东西:

ls -al file.ext
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi

您需要注意管道命令,因为$?只为您提供管道中最后一个元素的返回码,因此,在代码中:

ls -al file.ext | sed 's/^/xx: /"

如果文件不存在,则不会返回错误代码(因为管道的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中。

461
paxdiablo

如果你想使用$ ?,你需要在每个命令之后检查它,因为$?每个命令退出后更新。这意味着如果执行管道,您将只获得管道中最后一个进程的退出代码。

另一种方法是这样做:

set -e
set -o pipefail

如果你把它放在Shell脚本的顶部,看起来bash会为你处理这个问题。正如之前的一张海报所指出的那样,“set -e”会导致bash在任何简单命令上都出错。 “set -o pipefail”将导致bash退出,同时管道中的任何命令都会出错。

请参阅 此处此处 有关此问题的更多讨论。 这里 是set builtin上的bash手册部分。

213
Jeff Hill

set -e”可能是最简单的方法。只需将它放在程序中的任何命令之前。

50
Allen

如果你只是在没有参数的bash中调用exit,它将返回最后一个命令的退出代码。结合OR,如果上一个命令失败,bash应该只调用exit。但我没有测试过这个。

 command1 ||退出; 
 command2 ||出口;

Bash还会将最后一个命令的退出代码存储在变量$?中。

28
Arvodan
[ $? -eq 0 ] || exit $?; # exit for none-zero return code
25
chemila

http://cfaj.freeshell.org/Shell/cus-faq-2.html#11

  1. 如何在cmd1中获取cmd1|cmd2的退出代码

    首先,请注意cmd1退出代码可能不为零,但仍然不代表错误。例如,这发生在

    cmd | head -1
    

    您可能会观察到cmd1的141(或269与ksh93)退出状态,但这是因为cmd在读取一行后head -1终止时被SIGPIPE信号中断。

    要知道管道元素的退出状态cmd1 | cmd2 | cmd3

    一个。用zsh:

    退出代码在pipestatus特殊数组中提供。 cmd1退出代码位于$pipestatus[1]中的cmd3$pipestatus[3]退出代码中,因此$?始终与$pipestatus[-1]相同。

    湾用bash:

    退出代码在PIPESTATUS特殊数组中提供。 cmd1退出代码位于${PIPESTATUS[0]}中的cmd3${PIPESTATUS[2]}退出代码中,因此$?始终与${PIPESTATUS: -1}相同。

    ...

    有关更多详细信息,请参阅以下 链接

21
Vladimir Graschenko

对于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;
}
19
Konstantin Antselovich

在bash中这很容易,只需用&&将它们绑在一起:

command1 && command2 && command3

您还可以使用嵌套的if结构:

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi
11
Martin W
#
#------------------------------------------------------------------------------
# run a command on failure exit with message
# doPrintHelp: doRunCmdOrExit "$cmd"
# call by:
# set -e ; doRunCmdOrExit "$cmd" ; set +e
#------------------------------------------------------------------------------
doRunCmdOrExit(){
    cmd="[email protected]" ;

    doLog "DEBUG running cmd or exit: \"$cmd\""
    msg=$($cmd 2>&1)
    export exit_code=$?

    # if occured during the execution exit with error
    error_msg="Failed to run the command:
        \"$cmd\" with the output:
        \"$msg\" !!!"

    if [ $exit_code -ne 0 ] ; then
        doLog "ERROR $msg"
        doLog "FATAL $msg"
        doExit "$exit_code" "$error_msg"
    else
        #if no errors occured just log the message
        doLog "DEBUG : cmdoutput : \"$msg\""
        doLog "INFO  $msg"
    fi

}
#eof func doRunCmdOrExit
4
Yordan Georgiev