386

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

4

9 回答 9

502

在每个命令之后,可以在$?变量中找到退出代码,因此您将拥有如下内容:

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值,则将其存储。

于 2008-09-18T06:08:52.677 回答
225

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

另一种方法是这样做:

set -e
set -o pipefail

如果你把它放在 shell 脚本的顶部,看起来 Bash 会为你处理这个问题。正如之前的海报所指出的,“set -e”将导致 Bash 在任何简单命令上出现错误并退出。“set -o pipefail”也会导致 Bash 在管道中的任何命令上出现错误并退出。

有关此问题的更多讨论, 请参见此处此处。set内置的 Bash 手册部分。

于 2008-09-18T06:10:19.613 回答
54

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

于 2008-09-18T06:09:55.997 回答
31

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

命令1 || 出口;
命令2 || 出口;

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

于 2008-09-18T06:11:37.270 回答
26
[ $? -eq 0 ] || exit $?; # Exit for nonzero return code
于 2011-11-08T03:10:42.363 回答
21

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

  1. 如何获得cmd1in的退出代码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

于 2009-01-29T22:07:24.973 回答
19

对于重击:

# 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;
}
于 2008-09-18T06:18:53.760 回答
11

在 Bash 中,这很容易。只需将它们绑在一起&&

command1 && command2 && command3

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

if command1
   then
       if command2
           then
               do_something
           else
               exit
       fi
   else
       exit
fi
于 2008-09-18T06:08:32.850 回答
4
#
#------------------------------------------------------------------------------
# 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

}
于 2012-02-19T20:47:42.987 回答