我更喜欢编写solid 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
答案 0 :(得分: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
答案 1 :(得分: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也没有'失败',那么脚本将继续。
答案 2 :(得分:3)
set -o errexit bad_command || { resp_code=$? echo Bad Thing $resp_code happened }
答案 3 :(得分: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]}
来获取命令的退出状态,因为在这种情况下$?
不包含它。
答案 4 :(得分:2)
同意评论,如果您可以放弃errexit
,那么您可以轻松地将代码缩短为
bad_command || do_err_handle
good_command
我希望这会有所帮助。
答案 5 :(得分:2)
如果您要检测复合列表(或函数)的退出代码,并在其中应用errexit
和nounset
,则可以使用以下代码:
#!/bin/sh
set -eu
unset -v unbound_variable
f() {
echo "before false"
false
echo "after false"
}
g() {
echo "before unbound"
var=$unbound_variable
echo "after unbound"
}
set +e
(set -e; f)
echo error code of f = $?
set -e
echo still in main program
set +e
(set -e; g)
echo error code of g = $?
set -e
echo still in main program
上面应该为函数f
和g
打印非零错误代码。我想这可以通过任何POSIX shell进行。您还可以在EXIT陷阱中检测到错误代码,但此后外壳会退出。其他建议方法的问题在于,在测试此类复合列表的退出状态时,errexit
设置将被忽略。这是POSIX standard的引文:
执行复合列表时,-e设置将被忽略 在while,if,if或elif保留字之后 开始于!保留字,或AND-OR列表的任何命令 除了最后一个。
请注意,如果您已将函数定义为
f() (
set -e
...
)
这足以做到
set +e
f
echo exit code of f = $?
set -e
获取退出代码。
答案 6 :(得分:1)
只是想完成以下答案:https://stackoverflow.com/a/32201766/2609399 ,这是一个很好的捕获,顺便说一句。
我前段时间遇到过这个限制,并对其应用了类似的修复。请考虑以下代码示例。
invokeBashFunction() {
local functionExitCode="0"
/bin/bash -c "
set -o errexit
${*}
" || functionExitCode="${?}"
# add some additional processing logic/code
return "${functionExitCode}"
}
export -f invokeBashFunction
关于如何使用它的例子很少:
invokeBashFunction bad_function param1 "param 2" param3 && echo "It passed." || echo "It failed!"
if invokeBashFunction bad_function param1 "param 2" param3
then
echo "It passed."
fi
if ! invokeBashFunction bad_function param1 "param 2" param3
then
echo "It failed!"
fi
答案 7 :(得分:0)
如果你想知道bad_command的退出状态怎么办?
我认为最简单的方法是禁用errexit:
#!/bin/sh
set -o errexit
some_code_here
set +o errexit
bad_command
status=$?
set -o errexit
process $status
答案 8 :(得分:0)
在 bash 中你可以使用内置的陷阱,这是非常好的。看到 https://unix.stackexchange.com/questions/79648/how-to-trigger-error-using-trap-command
不确定它与其他炮弹有多便携..所以YMMV
答案 9 :(得分: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
答案 10 :(得分: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
答案 11 :(得分:0)
干净可靠的错误退出方式
command_that_error_exits || { echo "Line $LINENO: Failed with Error" 1>&2; exit 1;}
答案 12 :(得分:-1)
接受的答案是好的,但我认为可以重构得更好;更通用,更容易重构和阅读:
some_command_status=$(some_command && echo $? || echo $?)
VS
some_command && some_command_status=$? || some_command_status=$?