私はシェルスクリプトに初めて触れました。次のようなスクリプトがあります。
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
いくつかの注意:
<>
通常の代わりに非ブロックリダイレクトを使用してください<
。 Bashの一般的なリダイレクトブロック(FIFOが書き込まれるまで)Bashが開始される前であるread
ため、FIFOが書き込まれないとタイムアウトは発生しません。while [ true ]
文字列 "true" が空でないかテストし、空でない場合は while ループを続行します。しかし、これは推測できるように、コマンドを実行すると常にtrueを返し、trueの場合はwhileループを続行することを意味しwhile true
ます。true
この場合、結果はまったく同じです。それでもテスト構造がなければ、もっと明確になると思います[ ]
。while [ false ]
一緒に行くことを検討してくださいwhile false
...$((5*60))
算術拡張です。300
使いやすいですが、コマンドラインで算術演算を簡単に行う方法を示すのに役立ちます。
答え4
最も簡単な方法は、特定のファイルが存在するかどうかスクリプトをチェックすることです。その後、whileステートメントで短い間隔で数回眠ります。秒を短い間隔で使用する場合は、300回(= 5分)実行されるforループを使用します。擬似コード: for x=1 to 300 sleep 1 Second check_if_certain_file_exists did 外部的にはいつでもファイルを生成でき、スクリプトは1秒間隔で認識します。また、さまざまなコンテンツでファイルを生成し、そのコンテンツに応じてスクリプトにさまざまなタスクを実行させることもできます。