设置errexit选项时检测shell退出代码的正确方法

问题描述 投票:38回答:12

我更喜欢编写可靠的shell代码,因此总是设置errexit和nounset。

以下代码将在bad_command行停止

#!/bin/bash
set -o errexit ; set -o nounset
bad_command # stop here
good_command

我想抓住它,这是我的方法

#!/bin/bash
set -o errexit ; set -o nounset
rc=1
bad_command && rc=0 # stop here
[ $rc -ne 0 ] && do_err_handle
good_command

有没有更好或更清洁的方法

我的答案:

#!/bin/bash
set -o errexit ; set -o nounset
if ! bad_command ; then
  # error handle here
fi
good_command
shell
12个回答
37
投票

这个怎么样?如果你想要实际的退出代码......

#!/bin/sh                                                                       
set -e

cat /tmp/doesnotexist && rc=$? || rc=$?                                         
echo exitcode: $rc        

cat /dev/null && rc=$? || rc=$?                                                 
echo exitcode: $rc   

输出:

cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0

0
投票

@rrauenza给出的答案略有不同。由于&& rc=$?部分是他们的答案将始终等于&& rc=0,也可以在运行命令之前将rc设置为0。在我看来,结果更具可读性,因为变量是在其自己的代码行中预先定义的,并且只有在命令以非零退出状态退出时才会更改。如果还给出了nounset,那么现在很明显rc确实从未定义过。这也避免了将&&||混合在同一行,这可能会令人困惑,因为人们可能并不总是知道运算符的优先级。

#!/bin/sh                                                                       
set -eu

rc=0
cat /tmp/doesnotexist || rc=$?                                         
echo exitcode: $rc        

rc=0
cat /dev/null || rc=$?                                                 
echo exitcode: $rc   

输出:

cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0

0
投票

一种干净可靠的错误退出方式

command_that_error_exits || { echo "Line $LINENO: Failed with Error" 1>&2; exit 1;}

-1
投票

接受的答案是好的,但我认为可以重构为更好;更通用,更容易重构和阅读:

some_command_status=$(some_command && echo $? || echo $?)

some_command && some_command_status=$? || some_command_status=$?

18
投票

继续使用errexit。它可以帮助查找可能具有不可预测(并且难以检测)结果的错误。

#!/bin/bash
set -o errexit ; set -o nounset

bad_command || do_err_handle
good_command

以上工作正常。 errexit只需要线路通过,就像你使用bad_command && rc=0一样。因此,如果bad_command失败,上面带有'或'只会运行do_err_handle,只要do_err_handle也没有'失败',那么脚本将继续。


3
投票
set -o errexit

bad_command || {
  resp_code=$?
  echo Bad Thing $resp_code happened
}

3
投票

设置errexit并运行可能失败的命令时,避免退出Bash程序的常用方法是在命令之前使用!

命令运行后,$?不包含命令的退出状态。 (如果命令失败则包含0,否则包含1.)但是,PIPESTATUS数组确实包含命令的退出状态。无论是否设置了errexit,捕获可能失败的命令的退出状态的安全方法是:

! bad_command
rc=${PIPESTATUS[0]}

第二行可以简化为rc=$PIPESTATUS,但是Shellcheck会抱怨它。

如果(通常是这种情况)你不需要知道命令的退出状态,只要它成功或失败,那么@george的解决方案是好的,如果错误处理程序是一行的。对于多行错误处理程序,一个很好的选择是:

if ! bad_command ; then
    # Handle errors here
fi

请注意(除非在非常特殊的情况下)使用`bad_command`(如问题中所示)而不是普通的bad_command是不正确的。如果在错误处理代码中需要,可以使用${PIPESTATUS[0]}来获取命令的退出状态,因为在这种情况下$?也不包含它。


2
投票

同意评论,所以如果你可以放弃errexit,那么你可以轻松地缩短你的代码

 bad_command || do_err_handle
 good_command

我希望这有帮助。


2
投票

继续编写可靠的shell代码。

如果bad_command确实是一个命令,那么你就做对了。但要注意if,while,||,&&或!中的函数调用,因为errexit在那里不起作用。这可能很危险。

如果你的bad_command实际上是bad_function你应该这样写:

set -eu 

get_exit_code() {
    set +e
    ( set -e;
      "$@"
    )
    exit_code=$?
    set -e
}

...

get_exit_code bad_function

if [ "$exit_code" != 0 ]; then
    do_err_handle
fi

这在bash 4.0中运行良好。在bash 4.2中,您只能获得退出代码0或1。


0
投票

如果你想知道bad_command的退出状态怎么办?

我认为最简单的方法是禁用errexit:

#!/bin/sh
set -o errexit

some_code_here

set +o errexit
bad_command
status=$?
set -o errexit
process $status

0
投票

在bash中你可以使用内置的陷阱,非常好。见https://unix.stackexchange.com/questions/79648/how-to-trigger-error-using-trap-command

不确定它与其他炮弹有多便携..所以YMMV


0
投票

我拼凑了一个(希望)所有答案中的教科书示例:

#!/usr/bin/env bash

# exit immediately on error
set -o errexit

file='dfkjlfdlkj'
# Turn off 'exit immediately on error' during the command substitution
blah=$(set +o errexit && ls $file) && rc=$? || rc=$?
echo $blah
# Do something special if $rc
(( $rc )) && echo failure && exit 1
echo success
© www.soinside.com 2019 - 2024. All rights reserved.