是否有存储和计算返回值的bash脚本优雅的方式?是否有存储和计算返回值的bash脚本优雅的方式?(I

2019-05-12 04:59发布

我有一个相当复杂的一系列在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

表面上这看起来更加直截了当,但我意识到这是肮脏的。

如果其他逻辑并非如此复杂,我只是跑这个曾经,我知道我可以代替测试操作的嵌入,但是当你需要重复使用在其他位置的结果,而无需重新运行 ,这是不理想测试

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

即使这在技术上不是一个拍摄任务(虽然有些人可能认为可读性更好),但必要的数值测试语法似乎仍然很麻烦给我。 也许我只是强迫症。

我缺少一个更优雅的方式来分配的退出代码给一个变量,然后在IT部门以后呢?

Answer 1:

简单的解决方案:

output=$(complex_command)
status=$?

if (( status == 0 )); then
    : stuff with "$output"
fi

: more code

if (( status == 0 )); then
    : stuff with "$output"
fi

以上eleganter十岁上下

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


Answer 2:

你为什么不为需要以后发生的东西设置的标志?

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,在可变存储实际的命令是稍有可疑,和隐约触发击FAQ#50警告; 该代码读取(略&恕我直言)更自然地这样,但你必须要非常小心,你有过在任何时候变量的完全控制。 如果你有丝毫的怀疑,或许只是使用的字符串值和比较反对在每个路口的预期值。

[ "$cheeseballs" = "true" ] && java -crash -burn

等等等等; 或者你可以重构一些其他执行结构的布尔(选项的关联数组会是有意义的,但不能移植到POSIX sh ;一个PATH般的字符串是灵活的,但也许是太非结构化)。



Answer 3:

它的基础上,OP的澄清只有成功v故障(而不是具体的退出代码):

long_running_command | grep -q trigger_word || failed=1

if ((!failed)); then
  : stuff
else

: more code

if ((!failed)); then
  : stuff
else
  • 设置仅在发生故障时成功指示符变量(经由|| ,即,如果返回一个非零退出代码)。
  • 依赖于事实,没有定义的变量值为false算术条件(( ... ))
  • 必须注意的是,变量( $failed ,在这个例子中)没有意外地在其他地方初始化。

(在一个侧面说明,作为@nos已经在评论已经提到的,你必须要小心,涉及管道命令;从man bash (重点煤矿):

管道的返回状态是最后一个命令的退出状态除非pipefail选项启用 。 如果pipefail启用,管道的返回状态是最后一个(最右边)的命令的退出值与非零状态,或零,如果全部顺利命令退出。

要设置pipefail (这是默认关闭)时,使用set -o pipefail ; 打开它的电源,使用set +o pipefail 。)



Answer 4:

如果你并不需要保存具体的退出状态,命令刚是成功还是失败(例如:是否grep找到了比赛),我的使用假布尔变量存储结果:

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

笔记:

  • 外壳并没有真正有布尔(真/假)的变量。 什么实际发生在这里是“真”和“假”被存储为found_trigger变量字符串; 当if $found_trigger; then if $found_trigger; then执行,它运行的价值$found_trigger为命令,它只是碰巧的是, true命令总是成功和false命令总是失败,从而导致“正确的事”的情况发生。 在if ! $found_trigger; then if ! $found_trigger; then if ! $found_trigger; then ,将“!” 切换成功/失败状态,有效地充当布尔“不”。
  • if long_running_command | grep trigger_word; then if long_running_command | grep trigger_word; then就相当于运行命令,然后使用if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then检查其退出状态。 我觉得干净了一点,但你必须习惯的思维if是检查命令的成功/失败,不只是测试布尔条件。 如果“主动” if命令是不直观的给你,用一个单独的测试来代替。
  • 正如查尔斯·达菲在评论中指出,这一招执行数据的命令,如果你没有对这些数据完全控制......你没有什么比你的脚本是要干什么控制。 所以永远不要设置一个假布尔变量来比固定字符串以外的任何“真”与“假”,并确保在使用前设置变量。 如果你必须在脚本中的任何平凡的执行流程,将所有假布尔变量理智的默认值(即“真”或“假”)的执行流程之前变得复杂。

    不遵守这些规则可能会导致足够大,通过驱动一列货运火车的安全漏洞。



Answer 5:

如果你不关心确切的错误代码,你可以这样做:

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为假, 1为真在脚本中存储布尔我的首选方式。 if ((flag))模拟物Ç很好。

如果你对退出代码护理,那么你可以这样做:

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的明确的测试,而不是使用! ,不看错。

(是的, $?确实在这里得到正确的值。)



Answer 6:

嗯,这个问题有点模糊 - 如果可能的话,我建议考虑重构/简化,即

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 -只是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


文章来源: Is there an elegant way to store and evaluate return values in bash scripts?