我更喜欢编写可靠的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
这个怎么样?如果你想要实际的退出代码......
#!/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
@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
一种干净可靠的错误退出方式
command_that_error_exits || { echo "Line $LINENO: Failed with Error" 1>&2; exit 1;}
接受的答案是好的,但我认为可以重构为更好;更通用,更容易重构和阅读:
some_command_status=$(some_command && echo $? || echo $?)
与
some_command && some_command_status=$? || some_command_status=$?
继续使用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也没有'失败',那么脚本将继续。
set -o errexit bad_command || { resp_code=$? echo Bad Thing $resp_code happened }
设置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]}
来获取命令的退出状态,因为在这种情况下$?
也不包含它。
同意评论,所以如果你可以放弃errexit
,那么你可以轻松地缩短你的代码
bad_command || do_err_handle
good_command
我希望这有帮助。
继续编写可靠的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。
如果你想知道bad_command的退出状态怎么办?
我认为最简单的方法是禁用errexit:
#!/bin/sh
set -o errexit
some_code_here
set +o errexit
bad_command
status=$?
set -o errexit
process $status
在bash中你可以使用内置的陷阱,非常好。见https://unix.stackexchange.com/questions/79648/how-to-trigger-error-using-trap-command
不确定它与其他炮弹有多便携..所以YMMV
我拼凑了一个(希望)所有答案中的教科书示例:
#!/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