長い処理をターミナルで走らせるとき、末尾に & を付けてバックグラウンドへ回すのはよくある手です。
ところが、それだけだと端末を閉じた瞬間にプロセスごと消えることがあります。
この穴を埋めるのが nohup で、走っている処理を手元で操るのが jobs・bg・fg・disown という一群。
この記事では、& と nohup がそれぞれ何を担当しているのかを整理してから、ジョブ操作の基本をひととおり通します。
記事中の実行例は Ubuntu 22.04.5 LTS / GNU bash 5.1.16 / GNU coreutils 8.32 で試した結果です。
& と nohup は担当が違う
まず & から。
コマンドの末尾に付けると、シェルは処理の終了を待たずに次のプロンプトを返します。
つまり& は「待たない」という指示でしかなく、プロセスを守る力は持っていません。
対して nohup は、SIGHUP というシグナルを無視させるための道具。
SIGHUP は端末との接続が切れたときに飛んでくる信号で、多くのプロセスはこれを受け取ると素直に終了してしまいます。
& が「手を離す」役、nohup が「揺すられても落ちない」役、と分けて覚えると混ざりません。
同じ sleep を nohup 付きと素の & で1つずつ起動し、両方に SIGHUP を送って生死を比べてみました。
# nohup 付きと素の & で1つずつ起動する
sh -c 'nohup sleep 18 >/dev/null 2>&1 & echo NOHUP=$!; sleep 18 >/dev/null 2>&1 & echo PLAIN=$!'
NOHUP=25
PLAIN=26
# 両方に SIGHUP を送ってから生死を確認する
kill -HUP 25 26
pid 25 : 生存
pid 26 : 終了
nohup を付けた側だけが SIGHUP を越えて生き残りました。
裏を返すと、& だけで投げた処理は端末が切れた時点で道連れになるということ。
走っているジョブを見る・番号で止める
バックグラウンドに回した処理は、シェルが「ジョブ」として番号で管理します。
一覧は jobs、PID まで見たいときは jobs -l を使いましょう。
sleep 30 >/dev/null 2>&1 &
sleep 40 >/dev/null 2>&1 &
jobs -l
[1]- 9 Running sleep 30 > /dev/null 2>&1 &
[2]+ 10 Running sleep 40 > /dev/null 2>&1 &
kill %1
jobs
[1]- Terminated sleep 30 > /dev/null 2>&1
[2]+ Running sleep 40 > /dev/null 2>&1 &
肝は %1 という書き方で、PID ではなくジョブ番号を直接指定できる点。
ps で PID を探してから kill する手順を丸ごと省けるので、同じ端末で起動した処理ならこちらが速いです。
ただしジョブ番号はシェルごとに独立していて、別の端末や別セッションからは見えません。
PID から探す側のやり方は、こちらにまとめてあります。
止めて裏へ回す(Ctrl+Z と bg・fg)
フォアグラウンドで動かし始めてから「これは長いぞ」と気づく場面も少なくありません。
そのときは Ctrl+Z でいったん止めて、bg で裏へ送り直せば作業を続けられます。
次は Ctrl+Z の代わりに SIGTSTP を直接送った例。
sleep 20 >/dev/null 2>&1 &
kill -TSTP $!
jobs
[1]+ Stopped sleep 20 > /dev/null 2>&1
bg %1
[1]+ sleep 20 > /dev/null 2>&1 &
jobs
[1]+ Running sleep 20 > /dev/null 2>&1 &
Stopped から Running へ戻ったのが、そのまま読み取れると思います。
手元に引き戻したいときは fg %1 で、出力を目で追いながら Ctrl+C で止める、という流れも取れるわけです。
あとから守る disown
すでに & だけで起動してしまった処理を、後から守りたくなることもあるでしょう。
そこで使うのが disown で、ジョブをシェルの管理表から外し、終了時の SIGHUP 送信対象から除外します。
sleep 20 >/dev/null 2>&1 &
jobs
[1]+ Running sleep 20 > /dev/null 2>&1 &
disown %1
jobs
(何も表示されない)
jobs の一覧から消えたあとも、プロセス自体は動き続けています。
注意したいのは、disown が出力先の面倒までは見てくれないこと。
端末が閉じたあとに標準出力へ書こうとして落ちる事故を避けたいなら、起動の時点でファイルへリダイレクトしておくのが安全です。
nohup.out は必ず作られるわけではない
nohup を使うと nohup.out ができる、と覚えている人も多いでしょう。
ただし条件があって、標準出力が端末につながっているときだけ nohup.out へ追記されます。
# 端末上でそのまま実行した場合
nohup echo hello-from-nohup
nohup: ignoring input and appending output to 'nohup.out'
cat nohup.out
hello-from-nohup
反対に、パイプへ渡したり自分でリダイレクトしたりしていると nohup.out は作られません。
出力先を自分で決めてしまえば、どちらの経路でもログの場所に迷わずに済むので、実務では明示的なリダイレクトを勧めます。
nohup ./long_batch.sh > /var/log/long_batch.log 2>&1 &
この一行が、&・nohup・リダイレクトの3つを同時に片付ける定番形。
定期実行まで見据えるなら、cron 側の落とし穴も合わせて押さえておくと安全です。
最後に
& は待たないための記号、nohup は切断に耐えるための道具、jobs の一族は走っている処理を手元で操るための仕組み。
役割を分けて覚えておくと、「バックグラウンドに回したはずなのに消えていた」という詰まり方をしなくなります。
長い処理を投げる前に、出力先だけは先に決めておきましょう。
流しっぱなしにしたくない処理には、逆に上限時間を付ける手があります。
以上です。











コメントを残す