简体   繁体   English

如何在bash中循环退出状态

[英]How to get the exit status a loop in bash

I know how to check the status of the previously executed command using $?, and we can make that status using exit command.我知道如何使用 $? 检查先前执行的命令的状态,我们可以使用 exit 命令来设置该状态。 But for the loops in bash are always returning a status 0 and is there any way I can break the loop with some status.但是对于 bash 中的循环总是返回状态 0,有什么办法可以用某种状态打破循环。

#!/bin/bash
while true
do
        if [ -f "/test" ] ; then
                break ### Here I would like to exit with some status
        fi

done
echo $?  ## Here I want to check the status.

The status of the loop is the status of the last command that executes.循环的状态是执行的最后一个命令的状态。 You can use break to break out of the loop, but if the break is successful, then the status of the loop will be 0 .您可以使用 break 来跳出循环,但如果 break 成功,则循环的状态将为0 However, you can use a subshell and exit instead of breaking.但是,您可以使用子shell 并退出而不是中断。 In other words:换句话说:

for i in foo bar; do echo $i; false; break; done; echo $?  # The loop succeeds
( for i in foo bar; do echo $i; false; exit; done ); echo $? # The loop fails

You could also put the loop in a function and return a value from it.您也可以将循环放入一个函数中并从中返回一个值。 eg:例如:

in() { local c="$1"; shift; for i; do test "$i" = "$c" && return 0; done; return 1; }

Something like this?像这样的东西?

while true; do
    case $RANDOM in *0) exit 27 ;; esac
done

Or like this?或者像这样?

rc=0
for file in *; do
    grep fnord "$file" || rc=$?
done
exit $rc

The real question is to decide whether the exit code of the loop should be success or failure if one iteration fails.真正的问题是如果一次迭代失败,决定循环的退出代码是成功还是失败。 There are scenarios where one make more sense than the other, and other where it's not at all clear cut.在某些情况下,一种比另一种更有意义,而另一些则完全不明确。

The bash manual says: bash手册说:

while list-1; do list-2; done
until list-1; do list-2; done
  [..]The exit status of the while and until commands is the exit status
  of the last command executed in list-2, or zero if none was executed.[..]

The last command that is executed inside the loop is break .在循环执行的最后一个命令是break And the exit value of break is 0 (see: help break ).break的退出值为0(参见: help break )。

This is why your program keeps exiting with 0.这就是为什么您的程序一直以 0 退出的原因。

I think what you should be asking is: How can I wait until a file or a directory ( /test ) gets created by another process?我想你应该问的是:我怎么能等到文件或目录( /test )被另一个进程创建?

What you are doing up to now is polling with full power.到目前为止,您正在做的是全权轮询。 Your loop will allocate up to 100% of the processing power of one core.您的循环将分配最多 100% 的一个内核的处理能力。 The keyword is "polling", which is ethically wrong by the standards of computer scientists.关键词是“轮询”,按照计算机科学家的标准,这在道德上是错误的。

There are two remedies:有两种补救措施:

  1. insert a sleep statement in your loop;在循环中插入睡眠语句; advantage: very simple;优点:非常简单; disadvantage: the delay will be an arbitrary trade-off between CPU load and responsiveness.缺点:延迟将是 CPU 负载和响应能力之间的任意权衡。 ("Arbitrary" is ethically wrong, too). (“任意”在道德上也是错误的)。
  2. use a notification mechanism like inotify (see: man inotify );使用像inotify这样的通知机制(参见: man inotify ); advantage: no CPU load, great responsiveness, no delays, no arbitrary constants in your code;优点:没有 CPU 负载,响应速度快,没​​有延迟,代码中没有任意常量; disadvantage: inotify is a kernel API – you need some code to access it: inotify-tools or some C/Perl/Python code.缺点: inotify是一个内核inotify你需要一些代码来访问它: inotify-tools或一些 C/Perl/Python 代码。 Have a look at inotify and bash !看看inotify 和 bash

I would like to submit an alternative solution which is simpler and I think more elegant:我想提交一个更简单且我认为更优雅的替代解决方案:

(while true
 do
    if [ -f "test" ] ; then
        break
    fi  
 done

Of course of this is part of a script then you could user return 1 instead of exit 1
 exit 1
)
echo "Exit status is: $?" 

Git 2.27 (Q2 2020), offers a good illustration of the exit status in a loop, here within the context of aborting a failing test early (eg by exiting a loop), which is to say "return 1". Git 2.27(2020 年第二季度)很好地说明了循环中的退出状态,这里是在提前中止失败测试(例如通过退出循环)的上下文中,即“返回 1”。

See commit 7cc112d (27 Mar 2020) by Junio C Hamano ( gitster ) .请参阅Junio C gitster ( gitster ) 的commit 7cc112d (27 Mar 2020 )
(Merged by Junio C Hamano -- gitster -- in commit b07c721 , 28 Apr 2020) (由Junio C gitster合并gitster提交 b07c721 中,2020 年 4 月 28 日)

t/README : suggest how to leave test early with failure t/README :建议如何在失败时尽早离开测试

Helped-by: Jeff King帮助者:杰夫·金

Over time, we added the support to our test framework to make it easy to leave a test early with failure, but it was not clearly documented in t/README to help developers writing new tests.随着时间的推移,我们为我们的测试框架添加了支持,以便在失败的情况下尽早离开测试,但在t/README没有明确记录以帮助开发人员编写新测试。

The documentation now includes :文档现在包括

Be careful when you loop循环时要小心

You may need to verify multiple things in a loop, but the following does not work correctly:您可能需要在循环中验证多项内容,但以下内容无法正常工作:

 test_expect_success 'test three things' ' for i in one two three do test_something "$i" done && test_something_else '

Because the status of the loop itself is the exit status of the test_something in the last round , the loop does not fail when " test_something " for " one " or " two " fails.因为循环本身的状态是上一轮test_something的退出状态,所以当“ one ”或“ two ”的“ test_something ”失败时,循环不会失败。
This is not what you want.这不是你想要的。

Instead, you can break out of the loop immediately when you see a failure.相反,您可以在看到失败时立即跳出循环。
Because all test_expect_* snippets are executed inside a function, " return 1 " can be used to fail the test immediately upon a failure:因为所有test_expect_*片段都在函数内执行,所以“ return 1 ”可用于在失败时立即使测试失败:

 test_expect_success 'test three things' ' for i in one two three do test_something "$i" || return 1 done && test_something_else '

Note that we still && -chain the loop to propagate failures from earlier commands.请注意,我们仍然&& -chain 循环以传播来自早期命令的失败。

Use artificial exit code 🙂使用人工退出代码🙂

Before breaking the loop set a variable then check the variable as the status code of the loop, like this:在中断循环之前设置一个变量然后检查变量作为循环的状态代码,如下所示:

while true; do 
  if [ -f "/test" ] ; then
     {broken=1 && break; };
  fi
done
echo $broken #check the status with [[ -n $broken ]]

The break builtin for bash does allow you to accomplish what you are doing, just break with a negative value and the status returned by $? bash 内置的 break 确实允许你完成你正在做的事情,只是用一个负值和 $? will be 1:将是 1:

while true
do
    if [ -f "./test" ] ; then
            break -1
    fi
done
echo $?  ## You'll get 1 here..

Note, this is documented in the help for the break builtin:请注意,这在 break 内置的帮助中有记录:

help break

break: break [n] Exit for, while, or until loops. break: break [n] 退出 for、while 或 until 循环。

Exit a FOR, WHILE or UNTIL loop.退出 FOR、WHILE 或 UNTIL 循环。 If N is specified, break N enclosing loops.如果指定了 N,则中断 N 个封闭循环。

Exit Status: The exit status is 0 unless N is not greater than or equal to 1.退出状态:退出状态为 0,除非 N 不大于或等于 1。

You can break out of n number of loops or send a negative value for breaking with a non zero return, ie, 1您可以中断 n 次循环或发送负值以使用非零返回进行中断,即 1

I agree with @hagello as one option doing a sleep and changing the loop:我同意@hagello 作为一种选择来做睡眠和改变循环:

#!/bin/bash
timeout=120
waittime=0
sleepinterval=3
until [[ -f "./test" || ($waittime -eq $timeout) ]]
do
   $(sleep $sleepinterval)
   waittime=$((waittime + sleepinterval))
   echo "waittime is $waittime"
done

if [ $waittime -lt $sleepinterval ]; then
    echo "file already exists"
elif [ $waittime -lt $timeout ]; then
    echo "waited between $((waittime-3)) and $waittime seconds for this to finish..."
else
    echo "operation timed out..."
fi

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM