在bash中使用退出代码

Tim*_*eap 12 bash

我有一个bash脚本,对我的源代码运行三次检查,然后exit 0如果所有命令都成功,或者其中exit 1任何一个命令失败:

#!/bin/bash

test1 ./src/ --test-1=option
exit_1=$?

test2 ./src/ test-2-options
exit_2=$?

test3 ./src/ -t 3 -o options
exit_3=$?

# Exit with error if any of the above failed
[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]]
exit $?
Run Code Online (Sandbox Code Playgroud)

这段代码有效,但感觉过长且冗长.有什么方法可以做得更好吗?具体来说,我不满意:

  • 必须运行该命令,然后将退出代码分配给变量
  • 必须使用[[ ... ]],然后在下一行收集退出代码以退出
  • 必须明确地将变量比较为0 [[ $var -eq 0 ]],而不是将它们视为布尔值

理想情况下,最终结果将更具可读性:

exit_1=( test1 ./src/ --test-1=option )
exit_2=( test2 ./src/ test-2-options )
exit_3=( test3 ./src/ -t 3 -o options )

# Exit with error if any of the above failed
exit ( $exit_1 && $exit_2 && $exit_3 )
Run Code Online (Sandbox Code Playgroud)

我考虑过的一些事情:


将错误代码放入一行中的变量:

exit_1=$( test1 ./src/ --test-1=option )$?
exit_2=$( test2 ./src/ test-2-options )$?
exit_3=$( test3 ./src/ -t 3 -o options )$?
Run Code Online (Sandbox Code Playgroud)

这有效,并使这一点缩短,但我从来没有见过其他人使用过这个.这是明智/理智的事吗?这有什么问题吗?


只是运行测试,和&&他们在一起:

test1 ./src/ --test-1=option && \
test2 ./src/ test-2-options && \
test3 ./src/ -t 3 -o options
status=$?
Run Code Online (Sandbox Code Playgroud)

这并没有工作,因为bash的短路.如果test1失败,test2和test3不会运行,我希望它们全部运行.


检测错误并退出使用 || exit

[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] || exit 1
Run Code Online (Sandbox Code Playgroud)

这节省了一行尴尬的退出代码和变量,但重要的一点exit 1现在就在你可以错过的行的末尾.理想情况下,这样的事情会起作用:

exit [[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]]
Run Code Online (Sandbox Code Playgroud)

当然,这并不能工作,因为[[而是返回呼应它的输出.

exit $( [[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] ; echo $? )
Run Code Online (Sandbox Code Playgroud)

确实有效,但看起来仍然像一个可怕的污泥


没有明确地处理exit-codes-as-boolean

[[ $exit_1 && $exit_2 && $exit_3 ]]
Run Code Online (Sandbox Code Playgroud)

这不符合你希望它会做的事情.&&一起存储在变量中的三个返回代码的最简单方法是使用full $var -eq 0 && ....肯定有一个更好的方式?


我知道bash不是一个很好的编程语言 - 如果你甚至可以称之为 - 但是有什么方法可以让它变得不那么尴尬吗?

che*_*ner 11

您可以将退出代码bash的运算命令OR一起使用,并取消结果,如果任何代码非零,则退出代码为1.首先,一个例子:

$ ! (( 0 | 0 | 0 )); echo $?
0
$ ! (( 1 | 0 | 0 )); echo $?
1
Run Code Online (Sandbox Code Playgroud)

现在,你的脚本:

#!/bin/bash

test1 ./src/ --test-1=option; exit_1=$?
test2 ./src/ test-2-options;  exit_2=$?   
test3 ./src/ -t 3 -o options; exit_3=$?

# Exit with error if any of the above failed. No need for a final
# call to exit, if this is the last command in the script
! (( $exit_1 || $exit_2 || $exit_3 ))
Run Code Online (Sandbox Code Playgroud)

或者通常,您可以在运行任意数量的测试时累积退出代码:

#!/bin/bash

# Unfortunately, ||= is not an assignment operator in bash.
# You could use |=, I suppose; you may not be able to assign
# any meaning to any particular non-zero value, though.
test1 ./src/ --test-1=option; (( exit_status = exit_status || $? ))
test2 ./src/ test-2-options;  (( exit_status = exit_status || $? ))  
test3 ./src/ -t 3 -o options; (( exit_status = exit_status || $? ))
# ...
testn ./src "${final_option_list[@]}"; (( exit_status = exit_status || $? ))

exit $exit_status   # 0 if they all succeeded, 1 if any failed
Run Code Online (Sandbox Code Playgroud)


Chr*_*ton 5

我自己在寻找答案,因此决定采用类似于@chepner的方式,但不使用bash的算术表达式:

#!/bin/bash

failed=0
test1 ./src/ --test-1=option || failed=1
test2 ./src/ test-2-options || failed=1
test3 ./src/ -t 3 -o options || failed=1

# Exit with error if any of the above failed
if [ "$failed" -ne 0 ] ; then
    exit
fi
Run Code Online (Sandbox Code Playgroud)

您只需在开头设置一个标志,然后在任何语句失败的情况下将该标志设置为1。这||是关键。