MENU

実行中の処理が終わるのを待ってから次の作業を自動で始める方法

バックアップスクリプトが走り終わったらデプロイを始めたい、重い変換処理が完了したら自動でファイルを移動したい――こういった「処理の待ち合わせ」はLinuxの運用でよく出てきます。

あらかじめコマンドを並べておく(&& や ; でつなぐ)方法ではなく、すでに動いているプロセスの終了を検知して次へ進む方法に絞って整理します。

目次

同じシェルのバックグラウンドジョブには wait を使う

同じシェルセッションで & を使ってバックグラウンド実行した処理には、bash組み込みコマンドの wait がそのまま使えます。

./heavy_process.sh &
wait
echo "処理が終わりました。次の作業を開始します。"
./next_task.sh

引数なしの wait は、現在のシェルが持つすべてのバックグラウンドジョブが終わるまでブロックします。特定のジョブだけを待ちたい場合はPIDを渡します。

./job_a.sh &
PID_A=$!          # 直前にバックグラウンド起動したプロセスのPIDが入る

./job_b.sh &

wait "$PID_A"     # job_a だけ待つ
echo "job_a が完了しました。"
./next_task.sh

$! は直前のバックグラウンドプロセスのPIDを保持するシェル変数です。複数の処理を走らせておき、特定の1つが終わったら次へ進む、という制御がこれだけで書けます。

別のターミナルや別ユーザーが起動したプロセスはPIDループで監視する

wait は現在のシェルの子プロセスにしか効きません。別のターミナルで起動した処理や、他のユーザーが動かしているプロセスには使えないため、PIDをもとにした存在確認ループを使います。

# 対象プロセスのPIDを調べる(プロセス名で検索する例)
TARGET_PID=$(pgrep -f "heavy_process")

# PIDが存在する間は待機
while kill -0 "$TARGET_PID" 2>/dev/null; do
    sleep 5
done

echo "処理が終わりました。"
./next_task.sh

kill -0 は実際にシグナルを送らず、プロセスが存在するかどうかだけを確認します。プロセスが生きていれば終了コード0、存在しなければ非ゼロを返すため、ループの条件として利用できます。2>/dev/null でエラー出力を捨てておくと、プロセスが終了した瞬間のエラーメッセージが画面に出なくなります。

sleep 5 の秒数は処理の長さや求めるタイムラグに合わせて調整してください。頻繁に確認しても無駄なCPU使用につながります。

tail --pid を使うとループ不要で書ける

GNU coreutilsの tail コマンドには --pid オプションがあります。指定したPIDのプロセスが終了すると tail 自身も終了するという動作を利用して、ループを自分で書かずに待機できます。

TARGET_PID=$(pgrep -f "heavy_process")

# 対象PIDが終了するまでブロック
tail --pid="$TARGET_PID" -f /dev/null

echo "処理が終わりました。"
./next_task.sh

/dev/null を監視対象にするため出力は何も出ません。ループ変数や sleep の記述が不要になり、スクリプトがすっきりします。ただし --pid オプションはGNU tail 固有で、macOSのBSD tail では使えません。Linux環境専用と覚えておくと混乱を防げます。

処理側に完了フラグファイルを作らせる方法

待ち対象のスクリプトを編集できる場合は、処理の最後に専用のファイルを作成させ、そのファイルの出現を待つ方法も選択肢です。PIDは再利用されることがあるため(後述)、確実な終了検知が必要な場面に向いています。

# 処理側スクリプトの末尾に追加する
touch /tmp/job_done.flag
# 待ち側のスクリプト
FLAG=/tmp/job_done.flag

while [ ! -f "$FLAG" ]; do
    sleep 3
done

rm "$FLAG"        # 次回実行のために削除
./next_task.sh

フラグファイルのパスが固定だと複数の処理が同時に走ったときに衝突します。mktemp で一時パスを生成して処理側に渡すか、処理ごとにユニークな名前にするとより安全です。

よくあるつまずきと注意点

PIDが再利用されて別プロセスを待ち続けることがある

LinuxのPIDは上限(通常32768または4194304)に達すると小さい番号から再利用されます。対象プロセスが終了した直後に同じPIDで全く別のプロセスが起動すると、ループが抜けられなくなります。長時間の待機スクリプトでは、PIDに加えてプロセス名も確認するか、フラグファイル方式を採用することでこのリスクを下げられます。

pgrep で複数のPIDがヒットする場合がある

pgrep -f "name" は名前が部分一致するすべてのプロセスを返します。複数行返ってきた場合、kill -0 "$TARGET_PID" の引数が複数になってエラーになることがあります。pgrep の結果が1件だけかどうかをあらかじめ確認するか、-n(最新のPIDのみ)や -o(最古のPIDのみ)オプションで絞り込んでから使うと安全です。

wait はサブシェルをまたいで動作しない

bash の wait は現在のシェルの子プロセスにのみ有効です。別のスクリプトが起動したバックグラウンドジョブ、systemdが管理するサービス、別ターミナルのプロセスには機能しません。対象が自シェルの子プロセスでない場合はPIDループか tail --pid を選んでください。

権限がないプロセスは kill -0 でも確認できない

root以外のユーザーが所有するプロセスに対して別ユーザーから kill -0 を実行すると、プロセスが存在していても「Operation not permitted」エラーで非ゼロが返ります。この場合はループが即座に抜けてしまい、正しく待機できません。/proc/<PID> ディレクトリの有無を確認する方法に切り替えると、シグナル送信権限がなくても存在確認できます。

# /proc を使う代替方法
while [ -d "/proc/$TARGET_PID" ]; do
    sleep 5
done

/proc/<PID> はカーネルが管理する仮想ディレクトリで、プロセスが生きている限り存在します。終了と同時に消えるため、存在確認の代わりに使えます。

「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。

ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。

>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)

※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次