使用“ while true”来保持脚本存活是个好主意吗?


19

我只是从另一个世界跳到Unix,想知道是否

while true
do
  /someperlscript.pl
done

perl脚本本身内部具有一个文件夹/文件监视程序,当在目标位置中更改文件时将执行该文件夹/文件监视程序。

这(while true)个好主意吗?如果不是,那么首选的可靠方法是什么?

TIA

编辑:由于这似乎已经引起了一定的兴趣,这里是完整的方案。perl脚本本身使用文件监视程序监视目录。收到新文件(它们通过rsync到达)后,它将拾取新文件并进行处理。现在,传入的文件可能已损坏(不要问..来自树莓派),有时该进程可能无法对其进行处理。我不知道为什么,因为我们还不了解所有情况。

但是-如果该进程确实由于某种原因失败,我们希望它能够启动并运行并处理下一个文件,因为下一个文件与可能导致错误的上一个文件完全无关。

通常,我会使用某种形式的catch all并将所有代码包装在它周围,以免它崩溃。但是不确定perl。

据我了解,使用诸如supervisor之类的方法是一种很好的方法。


9
这是Linux还是UNIX?如果是Linux,则可能需要检查inotifyAPI,以便避免在目标目录中等待文件更改的繁忙循环。
roaima 2015年

它的Linux ubuntu
Abhinav Gujjar

如果您知道文件在离开pi之前是好的,那么您可以运行诸如md5或sha1之类的校验和并将其与文件一起发送。然后,接收者将在尝试处理之前知道它是否有错误的文件。如果您不知道,那么您仍然可以在数据文件中构建某种部分校验或块校验和或类似的校验和,以确保数据完整性,因此您可以在进行可能失败的过程之前检查情况。一盎司的预防措施

Answers:


21

这取决于perl脚本返回的速度。如果返回很快,则您可能希望在两次执行之间插入一个小暂停,以避免CPU负载,例如:

while true
do
  /someperlscript.pl
  sleep 1
done

如果找不到脚本或立即崩溃,这也将防止占用CPU。

最好在perl脚本本身中实现循环,以避免出现这些问题。

编辑:

当您编写循环时,唯一的目的是在崩溃时重新启动Perl脚本,更好的方法是将其实现为受监视的服务,但是实现它的确切方法取决于操作系统。例如:Solaris smf,Linux systemd或基于cron的重启器。


10
您可以while sleep 1; do ...保存真正的通话。
拉斐尔·阿伦斯

4
@RaphaelAhrens确实,尽管那会稍微改变初始行为。备用服务器until ! sleep 1; do ...; done仍将在立即启动脚本时保存该内置调用。
jlliagre

4
完全可以完全同意,如果要执行此操作,应避免在循环内进行sleep调用来避免热循环。也同意事件驱动脚本(inotify等)将是更好的解决方案。但是,使用while循环本质上并不一定是邪恶的,除非它是无限的且炙手可热。我认为,更重要的问题可能是处理为什么perl脚本失败并需要重新启动的原因。
2015年

2
更好:sleep 1& /someperlscript.pl; wait
user23013

2
@EliahKagan好了。TBH,我从没使用过until指令,我将其与do/until外壳中不存在的假设循环相混淆。
jlliagre

13

关于使用的其他答案inotify是正确的,但不是此问题的答案。

的工艺主管,如supervisordupstart或者runit,是专为观看和重新启动服务,如果它崩溃的正是这个问题。

您的发行版可能带有内置的流程管理器。


11

while true可以用作通用的“永远循环”结构。就像其他答案所说的那样,循环的主体不应该为空,或者由于循环内部的命令不起作用而变为空。

如果您使用的是Linux,则可能需要使用像这样的命令inotifywait,这会使while循环更加简单:

while inotifywait -qqe modify "$DIRECTORY"
do
    process_the_directory "$DIRECTORY"
done

在这里,inotifywait命令将坐下并等待文件系统事件发生(在此示例中,当目录中的文件被写入时)。到那时,它成功退出并且循环主体执行。然后返回到再次等待。因为该inotifywait命令等待目录中发生某些事情,所以它比连续轮询目录要有效得多。


`(apt-get或yum)安装inotify-tools` +1很酷!
JJoao 2015年

4

将while 1移到perl脚本中(遵循@roaima建议)

#!/usr/bin/perl

 use Linux::Inotify2;

 my $inotify = new Linux::Inotify2 or die "unable to inotify: $!";

 $inotify->watch ("Dir", IN_MODIFY, ## or in_{acess,create,open, etc...}
   sub { my $e = shift;
     my $name = $e->fullname;
     ## whatever 
     print "$name was modified\n" if $e->IN_MODIFY;
  });

 1 while $inotify->poll;

1
如果脚本崩溃或由于某种原因被杀死,这不能解决重启要求。
jlliagre

@jlliagre,感谢您的评论。在答案中,我没有看到崩溃或死亡后预期行为的明确规范。这显然是一个有趣且相关的问题。目前,我将保持简单(如果脚本已死或被杀死,请保持死:)
JJoao 2015年

@jlliagre这就是例外。但是在这种情况下,Perl可能不是最合适的选择,因为它不支持异常机制。只是一个猜测。最好将脚本移植到支持异常的语言。当然,这完全取决于移植工作的大小。

@Nasha,在Perl中,具有信号处理程序,错误变量,Tyr :: Tiny等,我们可以做到!...但是-我讨厌异常处理和错误恢复:它们永远不会完成...
JJoao 2015年

1
@JJoao我同意错误恢复很少完成的事实。当然,要由开发人员来涵盖所有可能的情况。工业界的PLC也是如此,因此,毕竟这是完全有可能的;-)。

3

当您的perl脚本打算一直保持运行时,为什么要使用while结构呢?如果由于某些严重问题而使perl失败,则由while启动的新perl脚本可能会同样崩溃。又一次又一次。
如果您确实希望Perl重新开始,请考虑使用crontab和一个首先检查正在运行的实例的脚本。这样,您的脚本甚至可以在重启后启动。


2

通常,使用是没有问题的,while true因为它是一个很小的测试,仅在终止perl脚本之后才执行。请记住,根据所使用的Linux / Unix版本,脚本可能在注销时终止。在这种情况下,请考虑在脚本中使用循环并使用进行调用nohup并将其放在后台,即nohup myscript &

如果perl脚本终止的次数过多并导致CPU负载超过这个负载则由perl脚本负责,而不是由while true

另请参见man nohup


唯一的问题是潜在的热循环会提高CPU利用率。因此,我完全同意将睡眠呼叫置于循环中以使其驯服。
2015年

2

如果要管理流程,则可能需要调查流程管理器来进行。

在许多最新的系统中,您可以使用systemd监视您的进程(这是systemd优于经典init脚本的优势之一)。如果您选择的发行版不使用systemd,则可以使用daemontoolsmonit


mon如果监视和系统的笨拙笨拙和他们一样困扰我,那么这是一个简单的选择。
Anko 2015年

2

while循环实在不是一个好主意。那里没有逃脱-它会永远永久地- 静态运行。环境中的任何事物都可能发生变化,并且不会受到影响-这可能很糟糕。

例如,如果负责该while循环的shell可执行文件已升级,则内核将无法释放旧版本的磁盘空间,直到该脚本退出为止,因为该脚本需要在运行时一直保持描述符。不管是出于任何原因,shell都可能打开该文件以运行该循环-它们将一直保持打开状态,while直到运行为止。

而且,如果天哪,在运行该循环的外壳程序中任何地方都存在内存泄漏,即使是在最短的时间内,它也将继续静态地泄漏。它会不受限制地构建,唯一的办法是强行杀死它,然后重新启动它以稍后再做。

确实,这不是设置后台进程的方式-至少,我认为不是。相反,按照我的想法,应该有一个重置点-刷新脚本及其状态。最简单的方法是使用exec。您可以用一个新进程替换当前进程-保持相同的PID-但仍在运行一个进程。

例如,如果您的perl脚本在成功处理了某些受监视目录中的文件修改后应返回true:

#!/bin/sh
trap 'rm -rf -- "${ldir%%*.}"' 0 INT
_exec() case    $#      in
        (0)     exec env -  "PID=$$" "ldir=${TMPDIR:-/tmp}/." \
                            "$0" "$@";;
        (*)     export "$@" "boff=0" "lmt=30"
                exec        "$0" "$@";;
        esac
[ "$PID" = "$$" ] || _exec
[ -w "$ldir" ]  &&
case    $ldir   in
(*.)    until   mkdir -- "$ldir"
        do      :& ldir=$ldir$$$!
        done    2>/dev/null
;;
(*)     until   /someperlscript.pl ||
                [ "$((boff+=1))"  -ge "$lmt" ]
        do      [ -d "$ldir" ]     &&
                sleep "$boff"      || ! break
        done
;;esac  &&      _exec ldir PID

...或类似的东西。允许循环背后的基本机制每隔一段时间刷新一次的东西。


1

我赞成其中一些答案,但本能地对@WalterA的答案怀有最热烈的感情。好吧,直到我创建自己的答案之前,我一直在做...

就个人而言,我倾向于更新Perl脚本,以便它编写有关故障的描述性日志条目,并将警报发送给管理员。

如果Perl脚本打算继续运行,等待文件系统中的更改事件,为什么会失败?

如果没有失败,为什么还要担心将其包装在脚本中以无限重启?

如果存在配置问题或某种破坏性的依赖关系导致Perl脚本中止,则只需一遍又一遍地重新启动它就不可能使其突然开始工作。

你知道精神错乱的定义吧?(一遍又一遍地做同样的事情,期望得到不同的结果)。只是说。;-)


哈哈-我知道,我知道。但您知道-现实世界糟透了。无论如何-原因是它处理文件,有些文件甚至可能无法正确传输。我们尚不知道它可能失败的各种原因。我看到了您关于记录错误的观点,但是我仍然需要备份它,以便通过有监督的方式处理下一个文件
Abhinav Gujjar 2015年

如果可以捕获到异常,则可以记录它们,然后继续进行处理,甚至返回并偶尔重试失败的文件?也许失败的文件被另一个用户或进程锁定,当你试图对其进行处理,等等
克雷格
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.