スクリプトを生き続けるために「while true」を使用するのは良い考えですか?


19

私はただ別の世界からunixに飛び込んでいて、

while true
do
  /someperlscript.pl
done

perlスクリプト自体には、ターゲットの場所でファイルが変更されたときに実行されるフォルダー/ファイルウォッチャーが内部にあります。

これwhile trueは良いアイデアですか?そうでない場合、好ましい堅牢なアプローチは何ですか?

TIA

編集:これはかなりの関心を生み出したように見えるので、ここに完全なシナリオがあります。perlスクリプト自体は、ファイルウォッチャーを使用してディレクトリを監視します。新しいファイルを受信すると(rsyncを介して到着します)、新しいファイルを取得して処理します。現在、着信ファイルが破損している可能性があり(質問しないでください。ラズベリーパイから来ています)、時にはプロセスがそれを処理できないことがあります。私たちはまだすべてのシナリオを認識していないので、正確な理由はわかりません。

ただし、プロセスが何らかの理由で失敗した場合、次のファイルはエラーを引き起こした可能性のある前のファイルとは完全に無関係であるため、実行して次のファイルを処理する必要があります。

通常、何らかのキャッチオールを使用し、コード全体をラップして、クラッシュしないようにしました。しかし、perlには確信がありませんでした。

私が理解したことから、supervisordのようなものを使用することはこのための良いアプローチです。


9
これはLinuxですか、UNIXですか?Linuxの場合は、inotifyAPI を確認して、ターゲットディレクトリでファイルが変更されるのを待機するビジーループを回避することができます。
ロアイマ

そのLinux、ubuntu
Abhinav Gujjar

piを離れる前にファイルが正常であることがわかっている場合は、md5やsha1などのチェックサムを実行して、ファイルとともに送信するだけです。その後、受信者は、処理を試みる前に不良ファイルを取得したかどうかを確認します。それがわからない場合でも、データファイルに何らかの部分的なチェックサムやブロックチェックサムなどをデータファイルに構築して、データの整合性を保証することができます。オンスの予防
ジョー

回答:


21

それは、perlスクリプトが戻る速度に依存します。すぐに戻る場合、CPU負荷を回避するために、実行の間に小さな一時停止を挿入することができます。例:

while true
do
  /someperlscript.pl
  sleep 1
done

これにより、スクリプトが見つからないか、すぐにクラッシュした場合にもCPUが占有されるのを防ぐことができます。

これらの問題を回避するには、perlスクリプト自体にループを実装することをお勧めします。

編集:

ループのみの目的は、クラッシュした場合にperlスクリプトを再起動することであるため、監視サービスとして実装することをお勧めしますが、正確な方法はOSに依存します。例:Solaris smf、Linux systemd、またはcronベースのリスターター。


10
while sleep 1; do ...真の呼び出しを保存することができます。
ラファエルアーレンス

4
@RaphaelAhrens確かに、初期動作はわずかに変わりますが。代替until ! sleep 1; do ...; doneは、スクリプトをすぐに開始しながら、その組み込み呼び出しを保存します。
-jlliagre

4
これを行う場合は、ループ内のスリープコールでホットループを回避する必要があることに完全に同意します。また、イベント駆動型スクリプト(inotifyなど)の方が優れたソリューションであることに同意します。しかし、whileループを使用することは、無限で熱くない限り、本質的に悪ではありません。より重要な問題は、おそらくperlスクリプトが失敗し、再起動する必要がある理由を扱うことだと思います。
クレイグ

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

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

@jlliagreそれが例外です。しかし、Perlは例外メカニズムをサポートしていないため、このような場合には最適な選択肢ではないかもしれません。ただの推測。例外をサポートする言語にスクリプトを移植することをお勧めします。もちろん、移植作業がどれだけ大きいかにかかっています。

@ Nasha、Perlでは、シグナルハンドラ、エラー変数、Tyr :: Tinyなどを使用して、それを実行できます。...しかし-私は例外処理とエラー回復が嫌いです:それらは決して完全ではありません
...-JJoao

1
@JJoaoエラーリカバリがほとんど完了しないという事実に同意します。もちろん、考えられるすべてのケースをカバーするのは開発者の責任です。産業界のPLCでもそうであるため、結局のところ非常に可能性が高いに違いありません;-)。

3

あなたのperlスクリプトが常に実行し続けることを意図しているのに、なぜwhile構築を使用するのですか?重大な問題のためにperlが失敗すると、その間に起動された新しいperlスクリプトは同じくらい激しくクラッシュする可能性があります。もう一度、そして再び。
perlを最初からやり直したい場合は、crontabと、実行中のインスタンスを最初にチェックするスクリプトを検討してください。これにより、スクリプトは再起動後にも開始されます。


2

一般にwhile true、perlスクリプトが終了した後にのみ実行される小さなテストであるため、使用に問題はありません。使用しているLinux / Unixのバリアントによっては、ログオフ時にスクリプトが終了する場合があることに注意してください。そのような場合、スクリプトでループを使用することを検討し、それを呼び出してnohupバックグラウンドに置く、すなわちnohup myscript &

perlスクリプトが頻繁に終了し、CPU負荷が発生する場合、この負荷は.lではなくperlスクリプトに影響しますwhile true

詳細も参照man nohupしてください。


唯一の問題は、CPU使用率を高めるホットループの可能性です。したがって、ループ内にスリープコールを入れて、ループを抑えることに完全に同意します。
クレイグ

2

プロセスを管理する場合は、プロセスマネージャーを調べて管理することをお勧めします。

最近の多くのシステムでは、systemdを使用してプロセスを監視できます(これは、従来のinitスクリプトに対するsystemdの利点の1つです)。選択したディストリビューションがsystemdを使用しない場合、daemontoolsまたはmonitを使用できます。


monmonitとsystemdの巨大な巨大さがあなたと同じくらいあなたを煩わせるなら、それは単純な代替手段です。
アンコ

2

whileループは本当に良いアイデアではありません。そこに逃げ場はありません-それはただ永久に- 静的に実行されます。環境内でさまざまなものが変更される可能性がありますが、影響はありません。これは悪いことです。

たとえば、そのwhileループを担当するシェル実行可能ファイルがアップグレードされると、カーネルは、スクリプトが実行される限り記述子を保持する必要があるため、そのスクリプトが終了するまで古いバージョンのディスク領域を解放できません。それは、ループを実行する何らかの理由でシェルが開いている可能性のあるすべてのファイルに当てはまります-ループが実行さ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スクリプトが異常終了する原因となっている依存関係が壊れている場合、単純に何度も再起動しても、突然動作を開始することはありません。

狂気の定義を知っていますよね?(同じことを何度も繰り返し、異なる結果を期待します)。ただ言って。;-)


ハハ-私は知っている、知っている。しかし、あなたは知っている-現実の世界は吸う。とにかく-その理由は、ファイルを処理し、一部のファイルが正しく送信されない場合があるためです。失敗するさまざまな理由はまだわかりません。エラーをログに記録することについてのあなたの意見はわかりますが、supervisord
Abhinav Gujjarの

例外をキャッチできる場合は、例外をログに記録してから処理を続行でき、失敗したファイルに戻ってときどき再試行することもできますか?失敗したファイルは、他のユーザーまたはプロセスによって処理しようとしたときにロックされたなどの可能性があります
Craig
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.