シェルスクリプトが眠っている間、外部からどのように制御できますか?

シェルスクリプトが眠っている間、外部からどのように制御できますか?

私はシェルスクリプトに初めて触れました。次のようなスクリプトがあります。

while [ true ]
do
  sleep 5m
  # PART X: code that executes every 5 mins
done

実行中に(1)外部からプログラムを傍受し、(2)眠っているプロセスを停止し、すぐにパートXを実行する方法は?これを達成するためにSIGNALSを使用できますか?それとももっと良い方法がありますか?

この種の問題を解決する一般的な方向を教えてください。

答え1

処理時間はスクリプトの責任です。これが今日使用するためのものであっても、/bin/sleep将来は使用されない可能性があるため、実際に長期的に動作するという保証はありません。わかった 君が見たいできるそのような保証をしないでください。私のポイントは、スリープが実装の詳細なので、スクリプトの外部で直接スリープを終了してはいけません。代わりに、スクリプトが別の信号をキャプチャしてSIGUSR1スクリプトに送信するようにしてください。

簡単な例は次のとおりです。

#!/usr/bin/env bash

kill_the_sleeper () {
    # This probably isn't really needed here
    # If we don't kill the sleep process, it'll just
    # hang around in the background until it finishes on its own
    # which isn't much of an issue for "sleep" in particular
    # But cleaning up after ourselves is good practice, so let's
    # Just in case we end up doing something more complicated in future
    if [ -v sleep_pid ]; then
        kill "$sleep_pid"
    fi
}

trap kill_the_sleeper USR1 EXIT

while true; do
    # Signals don't interrupt foreground jobs,
    # but they do interrupt "wait"
    # so we "sleep" as a background job
    sleep 5m &
    # We remember its PID so we can clean it up
    sleep_pid="$!"
    # Wait for the sleep to finish or someone to interrupt us
    wait
    # At this point, the sleep process is dead (either it finished, or we killed it)
    unset sleep_pid

    # PART X: code that executes every 5 mins
done

それからあなたは引き起こす可能性がありますパート10走るkill -USR1 "$pid_of_the_script"pkill -USR1 -f my_fancy_script

このスクリプトは完璧とにかく、しかし、少なくとも単純な場合は良いでしょう。

答え2

他の端末でプロセス
を終了しますsleep

pkill -f "sleep 5m"

サイクルは続きます。


pkillオペレーティングシステムで利用できない場合は、次を使用してPIDを取得できます。

$ ps aux | grep '[s]leep'
username 14628  0.0  0.0   8816   672 pts/19   S+   08:33   0:00 sleep 5m

その後、kill PIDプロセスを終了します(ここkill 14628:)。


これは手動で終了するのに役立ちますが、通常スクリプトを使用している場合はコマンドのすべてのプロセスをsleep終了するので、良い解決策ではないかもしれません。sleep 5m


スクリプトを編集できる場合、より安全なオプションは次のとおりです。

PID をファイルに書き込むようにスクリプトを変更します。

TMP_DIR="$HOME/.cache/myscript/"
mkdir -p "$TMP_DIR"
while [ true ]
do
  sleep 5m &
  printf '%s' $! > "$TMP_DIR"/sleep.PID
  wait
  # PART X: code that executes every 5 mins
done

その後、次を実行できます。

kill $(< ~/.cache/myscript/sleep.PID)

たとえば、同じスクリプトの他のインスタンスがPIDファイルを上書きする可能性があり、要件と比較して十分に安定していない可能性があります。

答え3

@sitaramの使い方が好きですionotifywait。ただし、より一般的なインフラストラクチャ、つまりシェルreadコマンド(Bash / ksh / Zshでオプションのタイムアウトを含む)とFIFOを使用して同様の操作を実行できます。

待機ループは次のとおりです。

mkfifo /tmp/myfifo
while true; do
    read -t $((5*60)) <> /tmp/myfifo
    date
    echo "Executed every 5 minutes"
done

読み取りを早期に中断するには、FIFOに改行文字を書き込むだけです。

echo > /tmp/myfifo

いくつかの注意:

  1. <>通常の代わりに非ブロックリダイレクトを使用してください<Bashの一般的なリダイレクトブロック(FIFOが書き込まれるまで)Bashが開始される前であるreadため、FIFOが書き込まれないとタイムアウトは発生しません。
  2. while [ true ]文字列 "true" が空でないかテストし、空でない場合は while ループを続行します。しかし、これは推測できるように、コマンドを実行すると常にtrueを返し、trueの場合はwhileループを続行することを意味しwhile trueます。trueこの場合、結果はまったく同じです。それでもテスト構造がなければ、もっと明確になると思います[ ]while [ false ]一緒に行くことを検討してくださいwhile false...
  3. $((5*60))算術拡張です。 300使いやすいですが、コマンドラインで算術演算を簡単に行う方法を示すのに役立ちます。

答え4

最も簡単な方法は、特定のファイルが存在するかどうかスクリプトをチェックすることです。その後、whileステートメントで短い間隔で数回眠ります。秒を短い間隔で使用する場合は、300回(= 5分)実行されるforループを使用します。擬似コード: for x=1 to 300 sleep 1 Second check_if_certain_file_exists did 外部的にはいつでもファイルを生成でき、スクリプトは1秒間隔で認識します。また、さまざまなコンテンツでファイルを生成し、そのコンテンツに応じてスクリプトにさまざまなタスクを実行させることもできます。

関連情報