终止无限循环


52

我有一条命令,希望每次终止时都可以自动再次运行,因此我运行了以下命令:

while [ 1 ]; do COMMAND; done;

但是如果我不能停止循环,Ctrl-c那只会杀死COMMAND而不是整个循环。

我将如何实现类似的功能,但无需关闭终端就可以停止?


11
如果我正在使用bash,则只需按Ctrl-Z即可停止作业,然后使用“ kill%1”将其杀死。
Paul Cager 2012年

3
稍等片刻……Linus被引述为:“我们都知道Linux很棒,它在5秒内完成了无限循环。” -真的……再等几秒钟,它应该完成。
lornix

@PaulCager也为我工作!为什么在Ctrl-C不起作用的地方起作用?
Ciro Santilli新疆改造中心法轮功六四事件2014年

@cirosantilli它杀死了外部工作(bash“包装器”)。在某些情况下,它不会立即杀死“ COMMAND”,例如,如果将它作为背景,即使它的父母已去世,它也可能会潜逃而过。但是循环已经死了,这是重要的部分。
Orion 2014年

Answers:


37

检查命令的退出状态。如果命令被信号终止,则退出代码将为128 +信号编号。从bashGNU在线文档中

出于shell的目的,成功执行了以零退出状态退出的命令。非零退出状态表示失败。使用了这种看似违反直觉的方案,因此存在一种明确定义的方法来指示成功,并且有多种方法来指示各种故障模式。当命令终止于编号为N的致命信号时,Bash使用值128 + N作为退出状态。

POSIX还指定以信号终止的命令的值大于128,但似乎不像GNU那样指定其确切值:

因收到信号而终止的命令的退出状态应报告为大于128。

例如,如果您用control-C中断命令,则退出代码将为130,因为SIGINT在Unix系统上是信号2。所以:

while [ 1 ]; do COMMAND; test $? -gt 128 && break; done

9
应该提到的是,这并不能保证,实际上,许多应用程序都不会这样做。
克里斯·唐纳

1
@Kyle Jones:您可以链接到提到这一点的POSIX / GNU文档吗?
Ciro Santilli新疆改造中心法轮功六四事件2014年

@cirosantilli完成。
凯尔·琼斯

@KyleJones谢谢!在实践中仍然不适用于COMMAND = paplay alert.ogg,也许是因为paplay处理信号了吗?
Ciro Santilli新疆改造中心法轮功六四事件2014年

@cirosantilli是的,这就是原因。如果某个进程处理了该信号然后退出,则与该进程因未处理的信号终止而有所不同。
凯尔·琼斯


18

我会说最好将无限循环放在脚本中并在那里处理信号。这是一个基本的起点。我确定您要对其进行修改以适合。该脚本用来trap捕获ctrl- c(或SIGTERM),终止该命令(我在sleep这里用作测试)并退出。

cleanup ()
{
kill -s SIGTERM $!
exit 0
}

trap cleanup SIGINT SIGTERM

while [ 1 ]
do
    sleep 60 &
    wait $!
done

4
真好 这是我使用此技巧制作自动重启netcat包装程序的方式:trap "exit 0" SIGINT SIGTERM; while true; do netcat -l -p 3000; done
Douglas

2
如果将此trap方法添加到相同的(bash)脚本中而导致无限循环被杀死,请使用$$代替$!(请参阅此处
ardnew


8

如果您运行bash,-e它将在任何错误情况下退出:

#!/bin/bash -e
false # returns 1
echo This won't be printed

7

为什么不简单地

while [ 1 ]; do COMMAND || break; done;

或者在脚本中使用时

#!/bin/bash
while [ 1 ]; do
  # ctrl+c terminates COMMAND and exits the while loop
  # (assuming COMMAND responds to ctrl+c)
  COMMAND || break
done;

1
非常优雅的解决方案。但是,如果COMMAND总是返回成功退出状态,这不是唯一有效吗?
Howardh 2015年

是的@howardh,没错。
戴尔·安德森

3

我更喜欢另一种解决方案:

touch .runcmd; while [ -f ".runcmd" ]; do COMMAND; sleep 1; done

为了杀死循环,只需执行以下操作:

rm .runcmd && kill `pidof COMMAND`

2
  1. 您始终可以使用其PID终止进程,而无需关闭终端
  2. 如果要在无限循环中运行某些程序(例如守护程序),则最好将其放在后台
  3. while : 将创建一个无限循环并节省您编写 [ 1 ]

    while :; do COMMAND; done &

这将打印PID。如果使用退出提示符,ctrl+d则后台作业不会退出,以后您可以使用kill PID

如果您找不到PID,可以使用pstree -pa $USERpgrep -fl '.*PROCESS.*'帮助您找到它


0

使用trap-

exit_()
{
    exit
}

while true
do
    echo 'running..'
    trap exit_ int
done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.