我在狂欢中有一系列相当复杂的命令,最终返回有意义的退出代码。脚本稍后的各个地方需要有条件地分支命令集是否成功。
目前,我正在存储出口代码并以数字测试,类似的内容:
long_running_command | grep -q trigger_word
status=$?
if [ $status -eq 0 ]; then
: stuff
else
: more code
if [ $status -eq 0 ]; then
: stuff
else
由于某种原因,感觉应该更简单。我们存储了一个简单的退出代码,现在我们反复键入数值测试操作以在其上运行。例如,我可以作弊使用字符串输出而不是返回代码,该返回代码更容易测试:
status=$(long_running_command | grep trigger_word)
if [ $status ]; then
: stuff
else
: more code
if [ $status ]; then
: stuff
else
在表面上看起来更直截了当,但我意识到它很脏。
如果其他逻辑不是那么复杂,我只运行一次,我意识到我可以嵌入它代替测试操作员,但是当您需要在其他位置重复使用结果<strong时,这并不理想>不重新运行测试:
if long_running_command | grep -q trigger_word; then
: stuff
else
到目前为止,我唯一发现的是将代码分配为命令替代的一部分:
status=$(long_running_command | grep -q trigger_word; echo $?)
if [ $status -eq 0 ]; then
: stuff
else
甚至从技术上讲,这也不是一个镜头分配(尽管有些人可能认为可读性更好),但是对我来说,必要的数值测试语法对我来说似乎仍然很麻烦。也许我只是强迫症。
我是否错过了一种更优雅的方法来将退出代码分配给变量,然后再分支?简单的解决方案:
output=$(complex_command)
status=$?
if (( status == 0 )); then
: stuff with "$output"
fi
: more code
if (( status == 0 )); then
: stuff with "$output"
fi
或更多的Eleganter-ish
do_complex_command () {
# side effects: global variables
# store the output in $g_output and the status in $g_status
g_output=$(
command -args | commands | grep -q trigger_word
)
g_status=$?
}
complex_command_succeeded () {
test $g_status -eq 0
}
complex_command_output () {
echo "$g_output"
}
do_complex_command
if complex_command_succeeded; then
: stuff with "$(complex_command_output)"
fi
: more code
if complex_command_succeeded; then
: stuff with "$(complex_command_output)"
fi
或
do_complex_command () {
# side effects: global variables
# store the output in $g_output and the status in $g_status
g_output=$(
command -args | commands
)
g_status=$?
}
complex_command_output () {
echo "$g_output"
}
complex_command_contains_keyword () {
complex_command_output | grep -q "$1"
}
if complex_command_contains_keyword "trigger_word"; then
: stuff with "$(complex_command_output)"
fi
如果您不需要存储特定的退出状态,只是命令是成功还是失败(例如,grep
是否找到了匹配),我使用的是假的boolean存储结果的变量:
if long_running_command | grep trigger_word; then
found_trigger=true
else
found_trigger=false
fi
# ...later...
if ! $found_trigger; then
# stuff to do if the trigger word WASN'T found
fi
#...
if $found_trigger; then
# stuff to do if the trigger WAS found
fi
注意:
- 外壳实际上没有布尔(true/false)变量。这里实际发生的事情是" true"one_answers" false"被存储在fund_trigger变量中的字符串;当
if $found_trigger; then
执行时,它将$found_trigger
作为命令的值运行,而true
命令始终会成功,并且false
命令始终失败,从而导致"正确的事物"发生。在if ! $found_trigger; then
中,"!"切换成功/失败状态,有效地充当布尔。 -
if long_running_command | grep trigger_word; then
等于运行命令,然后使用if [ $? -ne 0 ]; then
检查其退出状态。我发现它有点清洁,但是您必须习惯于将if
视为检查命令的成功/失败,而不仅仅是测试布尔条件。如果"活动"if
命令对您不直观,请改用单独的测试。 正如查尔斯·达菲(Charles Duffy)在评论中指出的那样,此技巧将数据作为命令执行,如果您对该数据没有完全控制...您无法控制脚本的发展去做。因此,切勿将伪造的树树变量设置为固定字符串" True"one_answers" False"以外的任何事物,并确保在使用该变量之前确保设置该变量。如果脚本中有任何非平凡的执行流,请将所有假树个变量设置为理智的默认值(即" true"或" false") 执行流程变得复杂。
未能遵守这些规则可能会导致足够大的安全孔以驱动货运火车。
为什么不为以后需要发生的东西设置标志?
cheeseballs=false
nachos=false
guppies=false
command
case $? in
42) cheeseballs=true ;;
17 | 31) cheeseballs=true; nachos=true; guppies=true;;
66) guppies=true; echo "Bingo!";;
esac
$cheeseballs && java -crash -burn
$nachos && python ./tex.py --mex
if $guppies; then
aquarium --light=blue --door=hidden --decor=squid
else
echo SRY
fi
@charlesduffy在评论中指出的那样,将实际命令存储在变量中有点怀疑,并且模糊地触发了Bash FAQ#50警告;代码更自然地读取(稍微&amp; Imho),但是您必须非常小心,以使您始终对变量完全控制。如果您有丝毫疑问,也许只使用字符串值并与每个连接处的预期值进行比较。
[ "$cheeseballs" = "true" ] && java -crash -burn
等;或者,您可以重构布尔值的其他一些实现结构(一系列选项是有意义的,但不适合POSIX sh
; a PATH
-like字符串灵活,但也许太无结构化)。
基于OP的澄清,仅与成功v。失败有关(与特定的退出代码相反):
long_running_command | grep -q trigger_word || failed=1
if ((!failed)); then
: stuff
else
: more code
if ((!failed)); then
: stuff
else
- 仅在 fafe 上设置成功指导器变量(通过
||
,即,如果返回 non-Zero 退出代码)。 - 依靠以下事实:未定义的变量在算术条件
(( ... ))
中评估为 false 。 - 必须注意变量(在此示例中,
$failed
)并非在其他地方偶然地初始化。
(在附带说明,正如@nos在评论中已经提到的那样,您需要小心涉及管道的命令;从man bash
(强调我的):
管道的返回状态是 last 命令的退出状态 除非启用PipeFail选项。如果启用了管道, 管道的返回状态是最后一个(最右)命令的值 以非零状态退出,如果所有命令成功退出,则为零。
设置pipefail
(默认情况下关闭),使用set -o pipefail
;要将其关闭,请使用set +o pipefail
。)
如果您不关心确切的错误代码,则可以这样做:
if long_running_command | grep -q trigger_word; then
success=1
: success
else
success=0
: failure
fi
if ((success)); then
: success
else
: failure
fi
使用 0
进行false,而1
对于True是我在脚本中存储布尔值的首选方法。if ((flag))
模拟c很好。
如果您确实关心退出代码,则可以这样做:
if long_running_command | grep -q trigger_word; then
status=0
: success
else
status=$?
: failure
fi
if ((status == 0)); then
: success
else
: failure
fi
我更喜欢针对0的显式测试,而不是使用!
,该测试不正确。
(是的,$?
确实在此处产生正确的值。)
hmm,问题有点含糊 - 如果可能的话,我建议考虑考虑考虑考虑重构/简化,即
function check_your_codes {
# ... run all 'checks' and store the results in an array
}
###
function process_results {
# do your 'stuff' based on array values
}
###
create_My_array
check_your_codes
process_results
另外,除非您确实需要保存退出代码,否则无需 store_and_test - Just testrong> test_and_do ,即使用上面建议的案例语句或类似的案例语句:
run_some_commands_and_return_EXIT_CODE_FROM_THE_LAST_ONE
if [[ $? -eq 0 ]] ; then do_stuff else do_other_stuff ; fi
:)
Dale