シェルスクリプトで任意のプロセスの終了を待って、別のコマンドを実行させる方法(ループ無し)

Bash

はじめに

プロセスIDが特定できているが、このプロセスが終了したことを確認して次の処理を実行させたい場合にどうするのがよいでしょう?

プロセス終了を持って、Macの通知バーにメッセージを表示させたかったのですが、 while ループと sleep コマンドでプロセス終了を監視する以外にうまい方法がないかを調べてみました。

検証環境

$ uname -moi
x86_64 MacBookPro11,4 Darwin

$ bash -version | head -n 1
GNU bash, バージョン 5.0.7(1)-release (x86_64-apple-darwin18.5.0)

$ tail --version | head -n 1
tail (GNU coreutils) 8.31

tailコマンドに便利なオプションがあった!

tail コマンドのマニュアルにぴったりなオプションがありました。

    --pid=PID
           with -f, terminate after process ID, PID dies

該当のプロセスIDの処理が終了するまで tail -f を続けてくれるオプションになります。

ただし、今回は tail したいファイルはありませんので、ちょっとしたコツが必要です。

試してみる

まずはターミナルを開き、以下のようなファイルを作成します。

$ cat <<'EOF' >test.sh
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o noclobber

while true; do
  echo .
  sleep 1
done
EOF

$ chmod 700 test.sh

実行します。1秒おきに . が出力されるはずです。

$ ./test.sh
.
.
.
.

別のターミナルを開き、以下のコマンドを実行します。
test.sh コマンドを実行中のプロセスのPIDが出力されます。

$ ps aux | grep '[t]est.sh'
genzouw 48472   0.0  0.0  4298708   1564 s013  S+   12:43PM   0:00.03 bash ./test.sh

プロセスIDがわかったので、このプロセスの終了を待ち、終了後に任意のコマンド(ここでは echo)を実行させてみます。

$ tail --pid=48472 -f /dev/null && echo '終了したよ'

監視対象のファイルはありませんが、 --pid オプション指定時には -f オプションが必須ということでビットバケツを指定しています。

さて、この状態でもとのターミナルに戻り、コマンドを終了させます。(Ctrl + C をタイプ)
監視中のターミナルで echo コマンドが実行されるはずです。

$ tail --pid=48472 -f /dev/null && echo '終了したよ'
終了したよ

ひとこと

echo の代わりに terminal-notifier を組み合わせると、息の長いコマンドを実行してしまったあとで、簡単に終了通知を設定することができます。

Bash