Linuxでバックグラウンドジョブを効率的に管理する方法

Linuxの対話型Bashで、同じシェルから起動した処理を管理するには jobs -l、fg %1、bg %1 を使います。%1 はPIDではなく、そのシェルのジョブ番号1を表すジョブ指定です。フォアグラウンド処理へ Ctrl+Z を押すと通常は停止状態になり、bg %1 でバックグラウンド再開、fg %1 で端末前面へ戻せます。Ctrl+C は通常SIGINTを送り、停止ではなく終了を依頼します。不要なジョブはまずTERMを送り、応答しないことを確認した場合だけKILLを検討します。ジョブ表はシェルローカルで、別端末や再接続したシェルへそのまま引き継がれません。 確認ポイント:jobspecは現在の対話shellのjob tableだけで有効で、system-wideなPID識別子ではありません。

目次

対話shellのjobspecを小さなsleepで確認する

sleep 120 &
jobs -l

Bashは [1] 12345 のような通知を出し、jobs -l は [1]+ 12345 Running sleep 120 & のようにジョブ番号、現在ジョブ記号、PID、状態、コマンドを表示します。番号とPIDは実行ごとに変わります。+ は現在ジョブ、- はその次の候補です。

このsleepは端末入力を使わないため、バックグラウンド管理の確認に適しています。実アプリケーションでは、バックグラウンド状態で端末から読み込もうとするとSIGTTINで停止することがあります。対話プログラムを無理に bg へ送らず、非対話モードと入出力リダイレクトを確認します。

ジョブ指定%1とPIDを区別する

jobs -l
printf "job pid=%s\n" "$!"
kill -0 %1 && printf "job 1 exists\n"

%1 は現在のBashが持つジョブ番号1です。$! は直近にバックグラウンド起動したパイプラインのPIDで、別の非同期処理を起動すると変わります。fg、bg、Bash組み込みの kill はジョブ指定を理解しますが、外部コマンドや別シェルへ %1 を渡しても同じ対象を意味しません。

ジョブ番号を省略した fg や bg は現在ジョブを選ぶため、複数ジョブがある場面では意図しない対象を操作できます。操作前に jobs -l を再表示し、コマンド、状態、ジョブ番号を読み、fg %1 のように明示します。PIDで外部監視する場合も、ジョブ表との対応を記録します。

Ctrl+Zで停止し、bgで再開する

別の安全な練習として、フォアグラウンドで sleep 120 を実行し、端末で Ctrl+Z を押します。Bashが [1]+ Stopped sleep 120 のように表示したら、プロセスは終了ではなく停止状態です。次のコマンドで状態を確認し、バックグラウンドへ再開します。

jobs -l
bg %1
jobs -l

bg %1 の後は Running になります。Ctrl+Zは端末のsuspend文字として通常SIGTSTPをフォアグラウンドのプロセスグループへ送ります。プログラムがシグナルを扱う場合やジョブ制御が無効なシェルでは挙動が異なることがあります。停止中のプロセスは処理を進めないため、停止したまま放置せず、再開するか終了します。

fgで前面へ戻し、Ctrl+Cとの違いを見る

fg %1

このコマンドでsleepが再び端末のフォアグラウンドになります。完了を待つ代わりに Ctrl+C を押すと、通常はSIGINTが送られてsleepは終了し、プロンプトへ戻ります。続けて jobs -l を実行し、ジョブが消えたか Done または Terminated の通知後に消えることを確認します。

Ctrl+Zは一時停止、Ctrl+Cは割り込みによる終了要求であり、同じ「戻る」操作ではありません。編集途中の対話プログラムを誤ってCtrl+Zで止めた場合は、別の同名プロセスを起動せず jobs -l でStoppedを確認して fg %番号 へ戻します。終了してよい処理だけにCtrl+CまたはTERMを使います。

TERMを送り、必要な場合だけKILLへ進む

sleep 120 &
job_pid=$!
jobs -l
kill -TERM %1
wait "$job_pid"
printf "wait status=%s\n" "$?"

この例は現在シェルのジョブ1が今起動したsleepであることを jobs -l で確認してからTERMを送ります。複数ジョブがすでにある場合、番号は1とは限らないので表示された正確な指定へ置き換えます。wait の値はシグナル終了を示す128超になるのが一般的です。通知後に jobs -l から消えれば完了です。

TERMはアプリケーションが後処理を行って終了できる通常の要求です。数秒からアプリケーション規定の猶予を置き、jobs -l と ps -p "$job_pid" で残存を確認します。停止状態のジョブへTERMを送ったときは、実装やシグナル処理によって再開が必要な場合があります。対象の公式な終了手順を優先します。

if kill -0 "$job_pid" 2>/dev/null; then
  ps -p "$job_pid" -o pid=,stat=,lstart=,args=
  kill -KILL "$job_pid"
  wait "$job_pid"
fi

KILLは捕捉・無視できず、後処理をさせない最終手段です。TERM後も同じPID、開始時刻、コマンドの対象が残り、停止が必要だと確認した場合だけ、保存した正確なPIDへ送ります。PIDは再利用され得るので、古いメモの番号だけで実行しません。名前一致の全プロセスを一括終了する方法も、無関係なジョブを巻き込むため避けます。

jobsに表示されない理由

jobs は現在のシェルが管理するジョブ表を表示します。別のターミナル、別のSSH接続、親シェル、systemdが起動したプロセスは通常ここにありません。シェルを終了して再接続すると、以前のジョブ番号 %1 は再利用される可能性があり、前の処理を指す識別子として保存できません。

切断後も対話状態を再利用したい作業には、組織で許可された端末マルチプレクサを使います。無人で継続すべき処理は、標準入出力、終了コード、再試行、ログを定義したsystemdサービスやスケジューラへ移します。単にシェルのジョブを切断後も残すことと、監視される永続サービスは別の運用要件です。

disownを使う前に範囲を理解する

Bashの disown はジョブをシェルのジョブ表から外したり、SIGHUP扱いを変更したりできますが、プロセスをサービスへ変換しません。disown後は jobs で管理できず、終了コードを同じ方法で回収できないことがあります。ログ、PID、停止方法を用意せずに実行すると、切断後の追跡が難しくなります。

一時的な実験ではdisownせず、現在シェルで wait まで完了させる方が結果を確認できます。ログアウト後の継続が要件なら、nohupのSIGHUP範囲とsystemdやスケジューラの管理範囲を比較し、再起動後にも必要か、失敗時に再実行するか、所有者は誰かを定義します。

小さな実演の完了確認

  • sleep 120 & の直後に jobs -l でRunningとPIDを確認した。
  • フォアグラウンドsleepへCtrl+Zを送り、Stoppedになったことを見てから bg %番号 で再開した。
  • fg %番号 で前面へ戻し、終了する場合だけCtrl+Cを使った。
  • 不要なバックグラウンドsleepへTERMを送り、wait と jobs -l で終了を確認した。
  • 操作したジョブがこのシェルのものだと、番号・PID・コマンドで照合した。

停止jobを残さず終了する

sleepの実演はファイルや設定を変更せず、終了すれば残りません。bgやfgは実行位置を変えるだけで、プロセスの処理内容そのものを取り消しません。実アプリケーションが停止前にファイルへ書いた内容は、ジョブ制御で自動的に戻らないため、変更を伴うコマンドではアプリケーション固有の整合性確認が必要です。この記事で状態を変える対象は、確認したsleepジョブだけです。

jobspec操作を終える確認

  • ジョブ番号 %1 とOSのPIDを区別した。
  • 操作直前に jobs -l で対象と状態を確認した。
  • Ctrl+Zを停止、Ctrl+Cを割り込み終了として使い分けた。
  • TERMに猶予を与え、KILLは残存する正確なPIDへの最終手段に限定した。
  • 別シェルや切断後に同じジョブ表が残るとは考えていない。

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次