ジョブ制御入門 - jobs/fg/bg/Ctrl+Z
この記事でできるようになること
Ctrl+Zで実行中のコマンドを 一時停止 して端末を取り戻せるbg/fgで バックグラウンド ↔ フォアグラウンド を自在に往復できるjobsで現在の ジョブ一覧 を確認、%nで個別に指定できる&を末尾に付けて 最初からバックグラウンド実行 できる- 「端末が固まった」「終了したのにジョブが消えない」等の 典型的な詰まりどころ を抜けられる
対象読者: ターミナルで vim や長時間のコマンドを実行中に「ちょっとだけ別のコマンドを打ちたい」と思って詰まった経験のある方。Ctrl+C で全部止めてしまって悔しい思いをした方。
導入:リナが Ctrl+C で泣いた日
vim で長い設定ファイルを編集してる途中で、別のターミナルで ls したくなったんですけど、新しいターミナルを開く方法が分からなくて、つい Ctrl+C 押したら vim ごと終了しちゃって…未保存の変更が全部消えました…。Ctrl+Z(コントロール ジー)。Ctrl+C は「実行中のコマンドを 殺す」けど、Ctrl+Z は「実行中のコマンドを 一時停止して脇に置く」だけ。端末は手元に戻ってくるから別コマンドを打てるし、用が済んだら fg で元の作業に戻れる。Ctrl+Z(一時停止)/ bg(裏で再開)/ fg(前に戻す)/ jobs(一覧)。結論(実務の型)
- 端末を取り戻したいだけ →
Ctrl+Z→bg(裏で動かしたまま端末解放) - 一時停止してそのまま放置 →
Ctrl+Zだけ(停止状態) - 元の作業に戻る →
fg - 何が動いてるか分からなくなったら →
jobs
前提(対象環境)
- bash / zsh(ジョブ制御は POSIX シェルの標準機能)
dashや非対話シェルでは効かない(/bin/sh -c等)- 本記事のキー操作は端末上の 対話的セッション が前提
1. 「ジョブ」と「プロセス」は何が違う?
ls | grep .txt | sort をパイプで打つと、プロセスは 3 つ作られるけど、シェルから見ればこれは ジョブ 1 つ として扱う。Ctrl+Z を押すとシェルはジョブ単位で一時停止してくれる。ジョブ番号と PID の違い
| 種類 | 例 | 付与する人 | 用途 |
|---|---|---|---|
| ジョブ番号 | %1 |
シェル | fg %1 / kill %1 等で指定 |
| PID | 12345 |
カーネル | kill 12345 / ps の出力 |
覚え方: ジョブ番号には先頭に % を付ける。fg 1 ではなく fg %1、kill 1 ではなく kill %1(後者の kill 1 は PID 1 つまり init を殺そうとする危険なコマンドになる)。
2. Ctrl+Z:今すぐ一時停止する
vim でも tail -f でも python の対話モードでも、何でもいいから 1 つコマンドを起動して、画面が動いてる状態で Ctrl+Z を押してみよう。fg で解凍すれば何事もなく動き出すよ。試してみる
$ sleep 100
sleep 100 は 100 秒間何もせずに待つコマンド。端末がこの 100 秒間ロックされる。
ここで Ctrl+Z を押す。
^Z [1]+ Stopped sleep 100 $
[1]+ Stopped が出れば成功。$ プロンプトが戻ってきていることに注目。端末が手元に戻った。
[1]+ の意味
[1]= ジョブ番号 1 番+= 直近に操作したジョブ(fg / bg をデフォルトで適用する対象)Stopped= 停止中(CPU を使っていない)
この状態で別コマンドを打てる
$ ls $ pwd $ echo "別のことができる"
sleep は冷凍庫の中で停止したまま、自由に他のコマンドを実行できる。これが Ctrl+Z の威力。
3. fg:呼び戻す / bg:裏で再開する
fg は「前面に呼び戻して再開」、bg は「停止したまま裏で再開」。前者はあなたが画面に向き合って続ける、後者はあなたが別の作業を続ける。fg:フォアグラウンドへ戻す
さっきの sleep 100 を戻してみる。
$ fg
sleep 100
sleep の続きが始まり、再び端末がロックされる。残り時間を待つか、もう一度 Ctrl+Z で止めるか、Ctrl+C で殺すか。
bg:バックグラウンドで再開
「裏で動かしながら端末は使い続けたい」場合は bg。
$ sleep 100 ^Z [1]+ Stopped sleep 100 $ bg [1]+ sleep 100 & $
Stopped から &(バックグラウンドで実行中)に変わった。これで sleep は裏で時計を進めながら、あなたは他の作業ができる。
bg できないコマンドもある: vim のような対話的なフルスクリーンアプリは「裏でキー入力を待つ」ことができないため、bg すると停止状態に戻ってしまう(Stopped (tty input))。vim は Ctrl+Z で停止 → 用が済んだら fg の往復が基本パターン。
4. & を末尾に付ける:最初からバックグラウンド
& を付けて起動 するのが手っ取り早い。Ctrl+Z → bg の 2 ステップを 1 ステップで済ませるってことですか?Ctrl+Z → bg は「実行してから気が変わって裏に回す」、& は「最初から裏で動かす」っていう違い。& の使い方
$ sleep 100 & [1] 12345 $
[1] がジョブ番号、12345 が PID。即座にプロンプトが戻る。
$ jobs [1]+ Running sleep 100 &
Running 状態。停止せずに動いている。
よく使う実例:ログ収集を裏で
$ tail -f /var/log/syslog > mylog.txt & [1] 23456 $ # この間に別の作業 $ vim config.txt
tail -f を裏で動かしながら vim で設定編集、みたいな並行作業ができる。
& だけだと出力が混ざる: バックグラウンドジョブの標準出力は そのまま端末に流れる(リダイレクトしない場合)。あなたが他のコマンドを打ってる最中に突然出力が割り込んでくるので、長時間動かすなら > file 2>&1 でファイルへ逃がすこと。
5. jobs:今動いてるものを全部見る
jobs 一発。今のシェルが管理してるジョブを全部一覧で出してくれる。基本:jobs
$ sleep 100 & [1] 12345 $ sleep 200 & [2] 12346 $ vim notes.txt # Ctrl+Z で停止 $ jobs [1] Running sleep 100 & [2]- Running sleep 200 & [3]+ Stopped vim notes.txt
3 つのジョブが見える。
+= 直近操作したジョブ(fg / bg のデフォルト対象 =vim)-= その 1 つ前のジョブ(=sleep 200)
ジョブを個別に指定:%n
$ fg %1 # sleep 100 を前面に $ bg %3 # vim を裏に(停止状態のまま) $ kill %2 # sleep 200 を終了
% プレフィックスを忘れない
kill 1 は PID 1(init)を殺すコマンド。一般ユーザーでは権限不足で弾かれるが、root で実行するとシステム自体が止まりかねない。「ジョブを指定する時は必ず %」を体に染み込ませること。
jobs の便利オプション
| オプション | 効果 |
|---|---|
jobs -l |
PID も併せて表示 |
jobs -p |
PID だけを表示(スクリプト用) |
jobs -r |
実行中ジョブだけ |
jobs -s |
停止中ジョブだけ |
6. ターミナルを閉じたらジョブはどうなる?
nohup や disown、もっと根本的には tmux。nohup:起動時に SIGHUP を無視させる
$ nohup ./long_script.sh > output.log 2>&1 & [1] 34567
nohup を付けて起動すると、シェル終了時の SIGHUP を無視する。ログアウトしてもジョブは生き残る。
disown:起動後に「シェルの管理下から外す」
$ ./long_script.sh & [1] 45678 $ disown %1 $ exit # ターミナルを閉じても %1 は生き続ける
「& で動かし始めたあとに気が変わって長時間処理にしたい」場合は disown で OK。
より確実なのは tmux
nohup / disown は「ターミナル切断時にジョブが死なないようにする」だけ。一方 tmux は「画面ごとサーバ側に保存しておく」ので、後から tmux attach でその画面に戻って続きを見られる。長時間処理 + 後でログを見たいなら tmux 一択。→ tmux 入門
7. つまずきポイント集
ピンチ 1: Ctrl+C と Ctrl+Z を取り違える
症状: 一時停止のつもりだったのにジョブが消えた。
原因: Ctrl+C(SIGINT で終了)と Ctrl+Z(SIGTSTP で一時停止)を混同。
対処: 「C = Cancel(中止)、Z = Z 字に寝かせる(停止)」と覚える。Ctrl+Z は終了ではないので vim や editor で押しても作業内容は保たれる。
ピンチ 2: kill %1 を kill 1 と打って怖い思いをする
症状: kill 1 を実行して「Operation not permitted」と言われた。あるいは root で実行してシステムが止まりかけた。
原因: ジョブ番号と PID を取り違え、% を忘れた。kill 1 は PID 1(init / systemd)に終了シグナルを送る命令。
対処: ジョブ指定は必ず % を付ける。不安なら先に jobs -l で PID を確認するか、kill %%(最後にアクセスしたジョブ)を使う。
ピンチ 3: バックグラウンドジョブの出力が画面に乱入
症状: & で動かしたコマンドの出力が、他のコマンド入力中に画面に割り込んでくる。
原因: バックグラウンドでも標準出力・標準エラー出力はデフォルトで端末に流れる。
対処: > file 2>&1 & でファイルへ逃がす。/dev/null に捨てる場合は > /dev/null 2>&1 &。
ピンチ 4: vim を bg したら止まったまま動かない
症状: vim を Ctrl+Z → bg したけど、jobs で見ると Stopped (tty input) のまま動かない。
原因: vim は端末入力を待つアプリ。バックグラウンド(端末から切り離された状態)ではキー入力を受け付けられないため、シェルが自動的に停止状態に戻す。
対処: vim や top のような対話的フルスクリーンアプリは Ctrl+Z で停止 → 用が済んだら fg の往復だけで使う。bg には回さない。
8. 実用テンプレ:ジョブ制御の事故らない型
コピペ用:vim 編集中に別コマンドを打つ型
# 1. vim で編集中 $ vim config.txt # Ctrl+Z で一時停止 # 2. 端末が戻ってきたので別作業 $ ls /etc/ $ grep "error" /var/log/syslog # 3. vim に戻る $ fg
vim を閉じずに往復するのが要。「Ctrl+Z → 別作業 → fg」のリズム。
コピペ用:長時間処理を裏で動かす型
# 出力をファイルへ逃がしながら裏で実行 $ ./long_script.sh > output.log 2>&1 & [1] 12345 # 経過を確認 $ jobs $ tail -f output.log # 終了させたい場合 $ kill %1
> output.log 2>&1 を忘れると画面が荒れる。これとセットで覚える。
コピペ用:ログアウト後も継続させる型
# 起動時から nohup で $ nohup ./long_script.sh > output.log 2>&1 & [1] 12345 # あるいは起動後に disown $ ./long_script.sh > output.log 2>&1 & [1] 12345 $ disown %1 # tmux で画面ごと持ち越すのがより確実(推奨) $ tmux $ ./long_script.sh # Ctrl+b → d でデタッチ、後で tmux attach で復帰
ミニ課題で手を動かそう
課題 1: Ctrl+Z → fg の往復
$ sleep 30 # 5 秒経ったところで Ctrl+Z で一時停止 $ jobs # → [1]+ Stopped sleep 30 を確認 $ ls # 別コマンドを打ってみる $ fg # sleep に戻って残り時間を待つ
課題 2: 3 つのジョブを並行で動かす
$ sleep 100 & $ sleep 200 & $ sleep 300 & $ jobs # → [1] [2] [3] の 3 つが見えるはず $ kill %2 # 真ん中だけ終了 $ jobs # → [2] が消えていることを確認
課題 3: 出力をファイルへ逃がしながら裏で実行
$ (for i in 1 2 3 4 5; do echo "step $i"; sleep 1; done) > result.log 2>&1 & $ jobs # Running を確認 $ cat result.log # ファイルへ出力が溜まっていく # ジョブが終わったら自動的に # [1]+ Done ( for i in 1 2 3 4 5; ...) > result.log 2>&1 # と通知される
Ctrl+Z の安心感、半端ないですね…これさえ知ってれば vim を Ctrl+C で殺すような事故は二度と起きない。今日の 3 行まとめ
Ctrl+Zで一時停止 → 端末解放、戻るときはfg、裏で動かすならbg。これだけで vim を Ctrl+C で殺す事故が消える- ジョブ指定は必ず
%(kill %1≠kill 1)。PID と取り違えると init を殺しかねない - 長時間処理を裏で動かすなら
& + > file 2>&1を癖にして、ログアウトを跨ぐならnohup/disown/tmuxを選ぶ