15

我知道如何使用 $? 检查先前执行的命令的状态,我们可以使用 exit 命令来获得该状态。但是对于 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.
4

8 回答 8

27

循环的状态是执行的最后一个命令的状态。可以使用 break 跳出循环,但是如果 break 成功,则循环的状态为0. 但是,您可以使用子shell 并退出而不是中断。换句话说:

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

您还可以将循环放在一个函数中并从中返回一个值。例如:

in() { local c="$1"; shift; for i; do test "$i" = "$c" && return 0; done; return 1; }
于 2012-12-27T18:37:34.057 回答
3

像这样的东西?

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

还是像这样?

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

真正的问题是在一次迭代失败时决定循环的退出代码是成功还是失败。在某些情况下,一种情况比另一种更有意义,而另一些情况则完全不明确。

于 2012-12-27T18:37:34.007 回答
2

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.[..]

在循环执行的最后一个命令是break. 退出值为break0(见:)help break

这就是为什么您的程序一直以 0 退出的原因。

于 2015-12-03T08:37:05.073 回答
0

我想你应该问的是:我怎样才能等到文件或目录(/test)被另一个进程创建?

到目前为止,您正在做的是全功率轮询。您的循环将分配多达 100% 的一个内核的处理能力。关键字是“投票”,按照计算机科学家的标准,这在道德上是错误的。

有两种补救措施:

  1. 在循环中插入一个 sleep 语句;优点:非常简单;缺点:延迟将是 CPU 负载和响应能力之间的任意权衡。(“任意”在道德上也是错误的)。
  2. 使用通知机制,如inotify(见:)man inotify;优点:没有 CPU 负载,响应速度快,没​​有延迟,代码中没有任意常量;缺点:inotify是一个内核 API——你需要一些代码来访问它:inotify-tools或者一些 C/Perl/Python 代码。看看inotify 和 bash
于 2015-12-03T09:04:53.220 回答
0

bash 内置的 break 确实允许您完成您正在做的事情,只需使用负值和 $? 返回的状态中断即可。将是 1:

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

请注意,这记录在内置 break 的帮助中:

help break

break: break [n] 退出 for、while 或 until 循环。

退出 FOR、WHILE 或 UNTIL 循环。如果指定了 N,则中断 N 个封闭循环。

退出状态:退出状态为 0,除非 N 不大于或等于 1。

您可以跳出 n 次循环或发送一个负值以进行非零返回,即 1

我同意@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
于 2017-01-17T19:04:14.367 回答
0

我想提交一个更简单且我认为更优雅的替代解决方案:

(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: $?" 
于 2019-06-01T15:53:47.280 回答
0

Git 2.27(2020 年第二季度)很好地说明了循环中的退出状态,这里是在提前中止失败的测试(例如通过退出循环)的上下文中,即“返回 1”。

请参阅Junio C Hamano ( ) 的提交 7cc112d(2020 年 3 月 27 日(由Junio C Hamano 合并 -- --提交 b07c721中,2020 年 4 月 28 日)gitster
gitster

t/README: 建议如何在失败的情况下提前离开测试

帮助者:杰夫·金

随着时间的推移,我们向我们的测试框架添加了支持,以使早期失败的测试变得容易,但它没有明确记录在t/README帮助开发人员编写新测试的过程中。

文档现在包括

循环时要小心

您可能需要在一个循环中验证多项内容,但以下内容无法正常工作:

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

因为循环本身的状态就是test_something上一轮test_something的退出状态,所以“for one”或“ ”失败时循环不会two失败。
这不是你想要的。

相反,您可以在看到失败时立即跳出循环。
因为所有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
'

请注意,我们仍然&&-chain 循环以传播早期命令的失败。

于 2020-05-01T18:24:48.900 回答
0

使用人工退出代码

在打破循环之前设置一个变量,然后检查该变量作为循环的状态代码,如下所示:

while true; do 
  if [ -f "/test" ] ; then
     {broken=1 && break; };
  fi
done
echo $broken #check the status with [[ -n $broken ]]
于 2020-07-10T04:04:22.563 回答