【问题标题】:Exit Shell Script Based on Process Exit Code [duplicate]基于进程退出代码的退出 Shell 脚本 [重复]
【发布时间】:2008-09-18 06:03:48
【问题描述】:

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

【问题讨论】:

  • 硬方法:在每个命令后测试$?的值。简单的方法:将 set -e#!/bin/bash -e 放在 Bash 脚本的顶部。

标签: bash shell


【解决方案1】:
[ $? -eq 0 ] || exit $?; # Exit for nonzero return code

【讨论】:

【解决方案2】:

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

另一种方法是这样做:

set -e
set -o pipefail

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

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

【讨论】:

  • 这应该是最佳答案:这样做比使用PIPESTATUS 并在各处检查退出代码要容易得多。
  • #!/bin/bash -e 是启动 shell 脚本的唯一方法。如果您需要实际检查退出状态,您可以随时使用 foo || handle_error $? 之类的东西。
  • @DavisHerring 这在几个方面是不正确或具有误导性的。 shell 的-e 选项有一些令人惊讶的极端情况,因此在某些指南中甚至不鼓励使用它,尤其是对于初学者。在shebang线上指定它很脆弱;将set -e 放入脚本本身会更加健壮。
  • @tripleee:我不知道“一些指导方针”意味着我自己对这个主题的建议是“不正确的”。您是否建议使用 set -e 基于运行 bash …/foo 并失去选项?如果是这样,如果您选择从外部运行脚本,则有很多方法会错误运行脚本……
  • 确实如此,但根据定义,“唯一的方法”是不正确的。例如,stackoverflow.com/questions/19622198/… 接受的答案总结了一些建议反对set -e
【解决方案3】:
#
#------------------------------------------------------------------------------
# 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

}

【讨论】:

  • 很少有理由使用$*;改用"$@" 来保留空格和通配符。
【解决方案4】:

在每个命令之后,可以在 $? 变量中找到退出代码,因此您会看到类似:

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 中。

【讨论】:

  • 只有一行可移植代码中的相同功能:ls -al file.ext || exit $?([[]] 不可移植)
  • MarcH,我想你会发现[[ ]]bash 中非常便携,这就是问题的标记:-) 奇怪的是,ls 在@ 中不起作用987654340@ 所以它也不是可移植的,我知道似是而非,但它与您提出的论点相同。
  • 我知道这很古老,但需要注意的是,您可以通过数组 PIPESTATUS 获取管道中命令的退出代码(即,${PIPESTATUS[0]} 用于第一个命令,@987654343 @ 用于第二个,或${PIPESTATUS[*]} 用于所有退出状态的列表。
  • 需要强调的是,优雅和惯用的 shell 脚本很少需要直接检查 $?。你通常想要if ls -al file.ext; then : nothing; else exit $?; fi 之类的东西,当然@MarcH 说它等同于ls -al file.ext || exit $?,但如果thenelse 子句稍微复杂一些,它更易于维护。
  • [[ $rc != 0 ]] 会给你一个0: not found1: not found 错误。这应该更改为[ $rc -ne 0 ]。还可以删除 rc=$? 并仅使用 [ $? -ne 0 ]
【解决方案5】:

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

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

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

【讨论】:

    【解决方案6】:

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

    【讨论】:

    • @SwaroopCH set -e 如果脚本中的任何命令以错误状态退出并且您没有处理此错误,您的脚本将中止。
    • set -e 100% 等同于 set -o errexit,与前者不同的是,它可以被搜索到。搜索 opengroup + errexit 获取官方文档。
    【解决方案7】:

    在 Bash 中,这很容易。只需将它们与&& 绑定在一起即可:

    command1 && command2 && command3
    

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

    if command1
       then
           if command2
               then
                   do_something
               else
                   exit
           fi
       else
           exit
    fi
    

    【讨论】:

    • +1 这是我一直在寻找的最简单的解决方案。此外,如果您希望命令中出现非零错误代码,您也可以编写 if (! command)
    • 这是用于顺序命令的......如果我想并行启动这 3 个并在其中任何一个失败时杀死所有人怎么办?
    【解决方案8】:

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

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

      首先,请注意cmd1 退出代码可能不为零,但并不意味着错误。例如,这种情况发生在

      cmd | head -1
      

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

      了解管道元素的退出状态 cmd1 | cmd2 | cmd3

      一个。与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

    【讨论】:

    • 第一个链接坏了:“我们无法连接到 cfaj.freeshell.org 的服务器。”
    【解决方案9】:

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

    【讨论】:

    • 可能不应该“退出 0”,因为这表明成功。
    • exit_code=$?;echo "脚本因错误而中止";exit $exit_code
    猜你喜欢
    • 2013-01-26
    • 2021-10-23
    • 2017-04-08
    • 1970-01-01
    • 2019-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多