是否有一种优雅的方式来存储和评估bash脚本中的返回值



我在狂欢中有一系列相当复杂的命令,最终返回有意义的退出代码。脚本稍后的各个地方需要有条件地分支命令集是否成功。

目前,我正在存储出口代码并以数字测试,类似的内容:

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字符串灵活,但也许太无结构化)。

<)。 <)。/div>

基于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

相关内容

最新更新