シェルスクリプトで実行中のプロセスの終了を待機する方法(waitコマンド以外)
はじめに
シェルスクリプトで実行中のプロセスが終了を待機する方法について取り上げます。
簡単な方法として、 wait
コマンドを利用する方法がありますが wait
コマンドでは対応できないケースへの対処法についても取り上げます。
検証環境
$ uname -moi
x86_64 x86_64 GNU/Linux
$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)
wait
コマンドを利用する方法
まずは実行中のプロセスを作成
実行中のプロセスを以下のコマンドで作成します。
$ bash -c '
for i in {0..19}; do
echo "execute($i)."
sleep 1
done
' > log.txt &
急いで標準出力をリダイレクトした log.txt
の内容を確認します。
$ tail -f log.txt
execute(0).
execute(1).
execute(2).
execute(3).
execute(4).
execute(5).
execute(6).
execute(7).
execute(8).
execute(9).
execute(10).
execute(11).
execute(12).
execute(13).
execute(14).
execute(15).
execute(16).
execute(17).
execute(18).
execute(19).
20回出力されて処理が終了します。
では、今度はプロセスをバックグラウンドで立ち上げた後、 wait
します。
# 先程のコマンドを立ち上げて
$ bash -c 'for i in {0..9}; do echo "execute($i)."; sleep 1; done' > log.txt &
# 終了を待つと、約10秒後に "finish!" が表示させる
$ wait && echo finish!
[1]+ Done bash -c 'for i in {0..9}; do echo "execute($i)."; sleep 1; done' > log.txt
finish!
wait
コマンドの課題
wait
コマンドには制限があります。
待ち受ける対象のコマンドは、 wait
コマンドを実行するシェルの子プロセスでなければいけません。
今回のケースでは以下のようになっているため、 wait
が可能です。
* ログインシェル( `bash` )
* `bash -c 'for i in {0..9}; do echo "execute($i)."; sleep 1; done' > log.txt &`
* `wait`
別途起動したログインシェルや cron
のようなプロセスからは終了を監視し待機できません。
kill
コマンドを利用する
kill
コマンドを利用する方法があります。
kill
コマンドに -0
というオプションがあります。
$ man 2 kill
...
If sig is 0, then no signal is sent, but error checking is still performed;
this can be used to check for the existence of a process ID or process
group ID.
...
kill -0
コマンドは、停止シグナルを送信しません。
ただし、引数で渡されたプロセスIDに該当するプロセスが終了した場合には、エラーステータスを返却します。
これを用いてプロセスの終了監視をすることができます。
kill
コマンドを利用したプロセスの終了監視サンプル
早速試してみます。
監視対象のプロセスを以下のコマンドを使って起動します。
先程のコマンドに少しだけ手を加えました。/tmp/pid
に実行中のプロセスのPIDを書き込んでいます。
$ bash -c '
echo $$ >/tmp/pid
for i in {0..19}; do
echo "execute($i)."
sleep 1
done
'
上記のコマンドを実行中に別のターミナルを立ち上げ、以下の監視コマンドを実行します。
先程プロセスIDを書き込んだファイルを読み込み、 kill
コマンドの引数として渡しています。
$ while kill -0 $(cat /tmp/pid); do
echo check.
sleep 1
done \
&& echo "finish."
check.
check.
check.
check.
check.
check.
check.
bash: kill: (410) - No such process
finish.
先の監視対象プロセスの終了と同時に、監視プロセスも終了したと思います。
エラーメッセージが気持ち悪い場合には以下のように標準エラー出力を /dev/null
しましょう。
$ while kill -0 $(cat /tmp/pid) 2>/dev/null; do
echo check.
sleep 1
done \
&& echo "finish."
一定時間経過後に監視をタイムアウトさせる
監視プロセス側でも、「いつまでも待っていられない」というケースも有るかと思います。
その場合には、 timeout
コマンドを利用してやることができます。
以下の例は先ほどと同じコマンドを実行して監視を行いますが、 5秒 を過ぎたプロセスを停止させてみます。
timeout
コマンドの第一引数には「制限時間(秒)」を、第二引数以降には「コマンド」を指定します。
$ timeout 5 bash -c '
while kill -0 $(cat /tmp/pid) 2>/dev/null; do
echo check.
sleep 1
done \
&& echo "finish."
'
check.
check.
check.
check.
check.
ひとこと
ps
や pgrep
を使う方法もありますが、こちらの方法が /etc
ディレクトリ配下のシェルスクリプトに書かれていることが多いため紹介しました。
ディスカッション
コメント一覧
まだ、コメントがありません