为什么测试"$?" 一个反模式,看一个命令是否成功?

svy*_*vye 8 bash shell sh

在这里看到测试是否$?是零(成功)或其他东西(失败)是反模式,但我无法在其他任何地方找到它.

坚持维基百科反模式的定义:"反模式(或反模式)是对反复出现的问题的常见反应,这种反复出现的问题通常是无效的,并且可能会产生极大的反作用." 为什么这会是反模式?

Cha*_*ffy 13

这是一个反模式,因为如果您根本不需要记录退出状态,它会引入不存在的复杂性.

if your_command; then ...
Run Code Online (Sandbox Code Playgroud)

比错误要少得多

your_command
if [ "$?" -eq 0 ]; then ...
Run Code Online (Sandbox Code Playgroud)

有关可能出错的事情的示例:考虑陷阱,甚至echo添加用于调试,修改的新语句$?.对于读者而言,your_command在不改变逻辑流程的情况下,单独运行的行不能在其下面添加任何内容,这在视觉上并不明显.

那是:

your_command
echo "Finished running your_command" >&2
if [ "$?" -eq 0 ]; then ...
Run Code Online (Sandbox Code Playgroud)

...正在检查回声,而不是实际命令.


因此,在你真的情况下需要处理的方式退出状态不是马上在其值是否为零分支更精细的,你应该收集它在同一行:

# whitelisting a nonzero value for an example of when "if your_command" won't do.
your_command; your_command_retval=$?
echo "Finished running your_command" >&2 ## now, adding more logging won't break the logic.
case $your_command_retval in
  0|2) echo "your_command exited in an acceptable way" >&2;;
  *)   echo "your_command exited in an unacceptable way" >&2;;
esac
Run Code Online (Sandbox Code Playgroud)

最后:如果您将其括your_commandif语句内部,则会将其标记为已测试,这样您的shell就不会考虑非零退出状态set -eERR陷阱.

从而:

set -e
your_command
if [ "$?" -eq 0 ]; then ...
Run Code Online (Sandbox Code Playgroud)

...永远不会(除了许多角落案件和瘟疫set -e行为的警告)达到if声明之外的任何$?其他价值0,因为set -e在这种情况下会强制退出.相比之下:

set -e
if your_command; then ...
Run Code Online (Sandbox Code Playgroud)

...标记已your_command测试的退出状态,因此不会认为它会导致脚本退出set -e.

  • @KevinDTimm,确实如此。语言特征有用;在不需要的时候使用它是令人不悦的。 (3认同)
  • @WilliamPursell,区分不同类型的故障有时是有意义且有用的——我确实在生产代码中有些地方我想忽略错误 X 但不是错误 Y——并且虽然需要检查 `$?` 不是干净的代码,它不像尝试检查写入 stderr 的文本那么令人讨厌(以及随之而来的脆弱性、本地化问题等) (3认同)