【问题标题】:Shell scripting: die on any errorShell 脚本:出现任何错误都会死掉
【发布时间】:2010-09-26 23:50:02
【问题描述】:

假设一个 shell 脚本(/bin/sh 或 /bin/bash)包含几个命令。如果任何命令的退出状态失败,我如何才能干净地使脚本终止?显然,可以使用 if 块和/或回调,但有没有一种更简洁、更简洁的方法?使用 && 也不是一个真正的选项,因为命令可能很长,或者脚本可能包含循环和条件等重要内容。

【问题讨论】:

    标签: bash shell scripting


    【解决方案1】:

    使用标准的shbash,您可以

    set -e
    

    会的

    $ help set
    ...
            -e  Exit immediately if a command exits with a non-zero status.
    

    它也适用于zsh(据我所知)。它也应该适用于任何 Bourne shell 后代。

    使用csh/tcsh,您必须使用#!/bin/csh -e 启动您的脚本

    【讨论】:

    • 谢谢,这似乎是我想要的。我想我应该提高我的 Google fu,我想...... :)
    • 请注意,条件句中的命令可能会失败而不会导致脚本退出——这很重要。例如:如果 grep 某事 /some/where;然后:找到了; else : 没找到; fi 工作正常,无论是否在 /some/where 中找到某些东西。
    • 你说的是“标准sh”。这是否意味着它是POSIX?编辑:我查了一下,它是 POSIX:pubs.opengroup.org/onlinepubs/009695399/utilities/set.html
    【解决方案2】:

    也许你可以使用:

    $ <any_command> || exit 1
    

    【讨论】:

    • 此模式对于在单个命令失败时选择性退出以及如果您想先打印自定义错误消息非常有用。要执行后者,请使用 &lt;any_command&gt; || eval 'echo "Please ensure ..." 1&gt;&amp;2; exit 1' 之类的东西 注意:eval 技巧对于将 echoexit 命令组合在一起而不创建 sub-shell 是必要的;使用子外壳(如果使用括号会发生这种情况)会使exit 无效。
    • 生活和学习:Bash 具有命令分组功能,因此不需要我提到的eval 技巧。相反,使用&lt;any_command&gt; || { echo "Please ensure ..." 1&gt;&amp;2; exit; }(注意在关闭}之前需要;)。
    【解决方案3】:

    你可以检查 $?看看最近的退出代码是什么..

    例如

    #!/bin/sh
    # A Tidier approach
    
    check_errs()
    {
      # Function. Parameter 1 is the return code
      # Para. 2 is text to display on failure.
      if [ "${1}" -ne "0" ]; then
        echo "ERROR # ${1} : ${2}"
        # as a bonus, make our script exit with the right error code.
        exit ${1}
      fi
    }
    
    ### main script starts here ###
    
    grep "^${1}:" /etc/passwd > /dev/null 2>&1
    check_errs $? "User ${1} not found in /etc/passwd"
    USERNAME=`grep "^${1}:" /etc/passwd|cut -d":" -f1`
    check_errs $? "Cut returned an error"
    echo "USERNAME: $USERNAME"
    check_errs $? "echo returned an error - very strange!"
    

    【讨论】:

    • -1:这是一个非常常见的反模式。 @Barun 的解决方案更简单、更惯用。
    • 与上述相同的脚本,但作为一种更简洁的“更简洁的方法”(全部在一行上): foo=$(grep "^${1}" /etc/passwd) && foo=$( echo "$foo" | cut -d: -f1) && echo ok || echo not_ok (nb: 如果使用 bash,并且不限于 sh,可以获取管道任何部分的退出状态,这简化了 grep/cut 逻辑。)
    • +1:这允许在脚本中进行适当的上下文错误处理,而无需复杂的子shell。
    猜你喜欢
    • 2016-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    相关资源
    最近更新 更多