inotifywait -q -m -e close_write,create --recursive ../orgmode-parse-print |
while read -r filename event; do
echo $filename;
echo $event
sleep infinity;
done
上記の問題は永遠に「スリープ」状態であり、決して終了しないということです。sleep
他のイベントが発生した場合、プロセス(デフォルトではwhileループ(含む)の内容)を終了または再開するにはどうすればよいですか?
つまり、コマンドを実行して終了し(割り込みであると仮定)、ファイルが変更されたら再起動します。
sleep
ここではこれを例として使用します。実行中の実際のプロセスは長期実行プロセスです。
答え1
これは私にとって効果的です。
$ inotifywait -q -m -e close_write,create --recursive dir1/ | \
(
CNT=0;
while read -r filename event; do
echo "count: $CNT filename: $filename event: $event"; ((CNT++));
[ "$CNT" -eq 1 ] && exit;
done
)
はい
まず、サンプルディレクトリ構造を作成しました。
$ mkdir -p dir1/dir2/dir{3..5}
$ tree dir1/
dir1/
└── dir2
├── afile
├── dir3
├── dir4
└── dir5
4 directories, 1 file
その後、これを実行してディレクトリの表示を開始します。
$ inotifywait -q -m -e close_write,create --recursive dir1/ | ( CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename event: $event"; ((CNT++)); [ "$CNT" -eq 1 ] && exit; done )
次に、touch afile
このディレクトリからコマンドを実行します。
$ cd dir1/dir2
$ touch afile
$ touch afile
結果は次のとおりですinotifywait
。
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
2番目の「イベント」に達すると終了します。
問題とより良い解決策
(...while ...)
echo
パイプにサブシェルを使用する場合の1つの問題は、2番目のイベントが発生したときに2番目のメッセージが表示されないことです。問題ありません。次のように簡単にリファクタリングできます。
$ CNT=0; while read -r filename event; do echo "count: $CNT filename: $filename event: $event"; ((CNT++)); [ "$CNT" -eq 2 ] && break; done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
$
拡張:
$ CNT=0; \
while read -r filename event; do \
echo "count: $CNT filename: $filename event: $event"; ((CNT++)); \
[ "$CNT" -eq 2 ] && break; \
done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
$
バックグラウンドタスクがあります
while ...
ループ内でブロックされているアクションがある場合は、それらを導入して終了し、ループが処理できるようにtrap
バックグラウンドに配置できます。while ...
inotifywait
例:
$ cat ./notifier.bash
#!/bin/bash
trap 'kill $(jobs -p)' EXIT;
CNT=0
while read -r filename event; do
sleep 1000 &
echo "count: $CNT filename: $filename event: $event"; ((CNT++))
[ "$CNT" -eq 2 ] && break
done < <(inotifywait -q -m -e close_write,create --recursive dir1/)
実行中:
$ ./notifier.bash
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
./notifier.bash: line 1: kill: (30301) - No such process
sleep
そしてバックグラウンドプロセスの残りの部分はありません。
$ ps -eaf|grep [s]leep
$
trap
最後の調整について皆さんも気づいたでしょう。これにより、kill $(jobs -p)
次のように画面にゴミが投げられます。時には次のようになります。
./notifier.bash: line 1: kill: (30301) - No such process
次のように整理できます。
trap 'kill $(jobs -p) > /dev/null 2>&1' EXIT;
今実行すると:
$ ./notifier.bash
count: 0 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
count: 1 filename: dir1/dir2/ event: CLOSE_WRITE,CLOSE afile
$
引用する
答え2
新しい割り込みを受け取ったときにコードが実行しているすべてのタスクを終了すると仮定すると、次のbashスクリプトがこの目的に適しています。
#!/bin/bash
my_function () {
sleep infinity
}
declare -A cache
inotifywait -q -m -e close_write,create --recursive ../orgmode-parse-print |
while read -r filename event; do
if [ "${cache[pid_my_function]}" ]; then kill "${cache[pid_my_function]}"; fi
echo $filename
echo $event
my_function &
cache[pid_my_function]=$!
done
デフォルトでは、スクリプトはsleep infinity
関数が呼び出されたときに別のプロセスとして実行できるように、長いプロセス(とマークされています)を関数の内部に配置します&
。このコマンドは、$!
後で新しい割り込みが到着したときに終了できるように、変数にプロセス番号を出力します。
OBS:このスクリプトは、新しい割り込みを受け取ったときにコードが実行する操作を終了しますが、実際にそうしたいかどうかはわかりません。関数を使用すると、プロセスを終了せずに各割り込み呼び出しを別々のプロセスとして実行できるため、スクリプトがすべての割り込みで実行されて&
いることを確認できます。