ジョブ制御入門 - jobs/fg/bg/Ctrl+Z

ジョブ制御入門 - jobs/fg/bg/Ctrl+Z

この記事でできるようになること

  • `Ctrl+Z` で実行中のコマンドを止めて、端末を取り戻せる
  • `fg` と `bg` でフォアグラウンドとバックグラウンドを行き来できる
  • `jobs` と `%番号` で複数のジョブを見分けて操作できる

前提知識(先に読むと理解しやすい記事)

この記事でできるようになること

  • Ctrl+Z で実行中のコマンドを 一時停止 して、端末を取り戻せる
  • bg / fgバックグラウンドとフォアグラウンド を自由に行き来できる
  • jobsジョブの一覧 を確認し、%番号 で 1 つずつ指定できる
  • & を末尾に付けて 最初からバックグラウンドで実行 できる
  • 「端末が固まった」「終了したのにジョブが消えない」といった 詰まりどころ を抜けられる

対象読者: vim や長いコマンドの実行中に「ちょっとだけ別のコマンドを打ちたい」と思って詰まった方。Ctrl+C で全部止めてしまって悔しい思いをした方。

導入:リナが Ctrl+C で泣いた日

リナ: 先輩、聞いてください!vim で長い設定ファイルを編集していました。途中で ls も打ちたくなったんです。でも新しいターミナル入力したコマンドを解釈してコンピュータに実行させる対話プログラム。の開き方が分からなくて、つい Ctrl+C を押しました。
リナ: そうしたら vim ごと終了しちゃって...未保存の変更が全部消えました...。
ライニー先輩: あー、それは痛いね。でも安心して。それを防ぐキー操作があるよ。Ctrl+Z(コントロール ゼット)だ。
リナ: シー(C)じゃなくてゼット(Z)、ですか?
ライニー先輩: そう。Ctrl+C は「実行中のコマンドを 止めて終わらせる」。Ctrl+Z は「実行中のコマンドを 一時停止して脇に置く」だけなんだ。
ライニー先輩: 一時停止なら端末が手元に戻る。だから別のコマンドを打てる。用が済んだら fg で元の作業に戻れるよ。
リナ: えっ、そんな魔法みたいなのが…!じゃあ、あのとき vim を殺さずに済んだんですね。
ライニー先輩: そういうこと。この機能を「ジョブ制御」と呼ぶ。シェル(bash や zsh)が持っている基本機能の 1 つだよ。今日は 4 つだけ覚えよう。Ctrl+Z(一時停止)/ bg(裏で再開)/ fg(前に戻す)/ jobs(一覧) だね。

結論(実務の型)

  • 端末を取り戻したいだけ → Ctrl+Zbg(裏で動かしたまま端末を解放)
  • 一時停止してそのまま放置 → Ctrl+Z だけ(停止状態)
  • 元の作業に戻る → fg
  • 何が動いているか分からなくなったら → jobs

先に覚える「固まったように見えるとき」の抜け方

ジョブ制御では、画面が止まったように見える場面がある。次の表の順で試せば必ず抜けられる。

見えている状態 抜け方
コマンドが動いたままプロンプトコマンドの入力を待っている状態を示す記号(例 $ や #)。が出ない Ctrl+Z で一時停止する(プロンプトが戻る)
Ctrl+Z の直後で何をすべきか不明 fg で元に戻すか、bg で裏に回す。どちらも取り消せる
何が止まっているか分からない jobs で一覧を見る。fg %1 のように番号で選ぶ
どうしても終わらせたい jobs で番号を確認してから kill %1% を必ず付ける)

Ctrl+Z は終了ではない。押しても作業内容は消えないので、安心して試してほしい。

端末(ターミナル)とは

端末は「文字を打ち込んで、結果を文字で受け取る窓口」のこと。「ターミナル」「コンソール」も、ほぼ同じものを指す別の呼び方だよ。

この記事でいう「端末が戻る」は「$ の入力待ちに戻り、次のコマンドを打てる状態になる」という意味。その $ の記号をプロンプトと呼ぶ。

前提(対象環境)

  • bash / zsh(ジョブ制御は POSIX シェルの標準機能。dash にも jobs / fg / bg はある)
  • 非対話シェル(sh -c '...' やシェルスクリプトの中)ではデフォルトで無効
  • 本記事のキー操作は端末上の 対話的セッション が前提

1. 「ジョブ」と「プロセス」は何が違う?

結論: プロセスは OS 目線で動くプログラム単位。ジョブはシェル目線で打ったコマンド 1 単位。指定は %番号

リナ: そもそも「ジョブ」って何ですか?「プロセス」とは別ものですか?
ライニー先輩: 似ているけれど、見る立場が違うんだ。プロセス(process)は OS から見た「動いているプログラム 1 つ 1 つ」のこと。
ライニー先輩: 一方の ジョブ(job)は、シェルから見た「あなたが打ったコマンド 1 つの単位」だよ。
リナ: ジョブはシェル目線、プロセスは OS 目線、ということですね。
ライニー先輩: そう。例えば ls | grep .txt | sort を打つとする。プロセスは 3 つ作られる。でもシェルから見れば ジョブは 1 つ なんだ。Ctrl+Z を押すと、シェルはジョブ単位でまとめて一時停止してくれるよ。

フォアグラウンドとバックグラウンド

  • フォアグラウンド(前面、foreground): あなたのキー入力を受け取る側。実行中は端末が返ってこない
  • バックグラウンド(裏、background): 画面の裏で動く側。端末はあなたが自由に使える

レジに並ぶ列に例えると分かりやすい。フォアグラウンドは「今レジの前にいる人」。バックグラウンドは「番号札を持って別の場所で待っている人」だよ。

ジョブ番号と PID の違い

種類 付与する人 用途
ジョブ番号 %1 シェル fg %1 / kill %1 等で指定
PID 12345 カーネルOS の中核となるプログラム。ハードウェアとソフトウェアの橋渡しをする。 kill 12345 / ps の出力

覚え方: ジョブ番号には先頭に % を付ける。fg 1 ではなく fg %1kill 1 ではなく kill %1

kill 1 は「ジョブ 1 番」ではなく「PID 1 番のプロセス」への命令になる。PID 1 は systemd(システム全体の親玉)。一般ユーザーなら「Operation not permitted」で弾かれる。いずれにせよ、あなたが止めたかったジョブは止まらない。

2. Ctrl+Z:今すぐ一時停止する

結論: Ctrl+Z は実行中のジョブを終わらせず、一時停止する。端末がすぐ手元に戻り、別のコマンドを打てる。

ライニー先輩: 一番大事なキー操作だよ。vim でも tail -f でも何でもいい。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 なら停止したまま裏で再開する。vim は fg の往復が基本。

リナ: 停止したジョブは、どうやって動かせばいいですか?
ライニー先輩: 選択肢は 2 つ。fg(foreground の略)は「前面に呼び戻して再開」。bg(background の略)は「裏で再開」だよ。
リナ: 使い分けの基準は何ですか?
ライニー先輩: あなたが画面に向き合って続けるなら fg。あなたが別の作業を続けるなら bg だね。

fg:フォアグラウンドへ戻す

さっきの sleep 100 を戻してみる。

$ fg
sleep 100

sleep の続きが始まる。端末は再びふさがる。残り時間を待ってもいい。もう一度 Ctrl+Z で止めてもいい。

bg:バックグラウンドで再開

「裏で動かしながら端末は使い続けたい」なら bg を使う。

$ sleep 100
^Z
[1]+  Stopped                 sleep 100
$ bg
[1]+ sleep 100 &
$

表示が Stopped から &(バックグラウンドで実行中)に変わった。sleep は裏で時計を進める。あなたは他の作業ができる。

bg できないコマンドもある: vim のような画面全体を使う対話アプリは、裏でキー入力を待てない。そのため bg すると停止状態に戻ってしまう(Stopped (tty input))。vimCtrl+Z で停止 → 用が済んだら fg の往復で使うこと。

4. & を末尾に付ける:最初からバックグラウンド

結論: コマンドの末尾に & を付ければ最初から裏で実行できる。出力は > file 2>&1 で逃がすのが定石。

ライニー先輩: 最初から「これは裏で動かす」と決まっているなら、コマンドの末尾に & を付けて起動 すると早いよ。
リナ: Ctrl+Zbg の 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 一発。今のシェルが管理しているジョブを、全部一覧で出してくれるよ。

基本: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

ジョブを個別に指定:%番号

$ fg %1     # sleep 100 を前面に
$ bg %3     # vim を裏に(停止状態のまま)
$ kill %2   # sleep 200 を終了

kill の前に確かめること

kill は GUI の「ウィンドウを閉じる」と違い、保存するか聞いてくれない。編集中のファイルがあると、保存していない内容はそのまま消える。

  • 先に jobs で「本当にそのジョブでよいか」を確かめる
  • エディタなど作業中のジョブは、kill ではなく fg で戻して自分で保存してから終わらせる
  • 練習は sleep を相手にする。sleep には消えて困る中身が無いので、安心して試せる

% プレフィックスを忘れない

kill 1PID 1(systemd)へのコマンド になる。一般ユーザーなら権限不足で弾かれる。rootすべての操作が許可された特別な管理者ユーザー。 で実行しても systemd が設定を読み直す動きに入るだけで、狙ったジョブは止まらない。「ジョブを指定するときは必ず %」を体に覚えさせよう。

jobs の便利オプション

オプション 効果
jobs -l PID も併せて表示
jobs -p PID だけを表示(スクリプト用)
jobs -r 実行中ジョブだけ
jobs -s 停止中ジョブだけ

6. ターミナルを閉じたらジョブはどうなる?

結論: 既定では SIGHUP でジョブは終了する。生き残らせるには nohup / disown。より確実なのは tmux

リナ: バックグラウンドで動かしたまま、ターミナルを閉じたらどうなるんですか?
ライニー先輩: 普通は 終了する。ターミナルの窓を閉じたり SSH が切れたりすると、シェルに SIGHUP(ハングアップ信号) が届く。シェルはそれを配下のジョブにも伝えるんだ。
リナ: シグナルって何ですか?
ライニー先輩: シグナルは「プロセスに送る短い合図」のこと。「終わってください」「今すぐ止まれ」といった内容を、名前と番号で送るしくみだよ。
リナ: えっ、長い処理を裏で動かして油断していたら、全部消えちゃうんですか?
ライニー先輩: そう。それを防ぐのが nohupdisown だよ。もっと根本的に解決したいなら 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 で対応できる。

より確実なのは tmux

nohup / disown は「ターミナル切断時にジョブが終了しないようにする」だけ。一方 tmux は「画面ごとサーバ側に保存しておく」。だから後から tmux attach でその画面に戻り、続きを見られる。長い処理を流して後でログを見たいなら tmux が向いている。→ tmux 入門

7. つまずきポイント集

結論: Ctrl+C と Ctrl+Z の混同、kill 1 事故、出力の乱入、vim の bg 失敗が代表的な落とし穴。

リナ: 先輩、よくある落とし穴ってありますか?
ライニー先輩: 4 つ覚えておけば、だいたい網羅できるよ。

ピンチ 1: Ctrl+C と Ctrl+Z を取り違える

症状: 一時停止のつもりだったのに、ジョブが消えた。

原因: Ctrl+C(SIGINT で終了)と Ctrl+Z(SIGTSTP で一時停止)を混同した。

対処: 「C = Cancel(中止)、Z = Z 字に寝かせる(停止)」と覚える。Ctrl+Z は終了ではない。vim やエディタで押しても作業内容は保たれる。

ピンチ 2: kill %1 を kill 1 と打って怖い思いをする

症状: kill 1 を実行して「Operation not permitted」と言われた。止めたかったジョブは動いたままだった。

原因: ジョブ番号と PID を取り違えて、% を忘れた。kill 1 は PID 1(systemd)へシグナルを送る命令になる。

対処: ジョブ指定には必ず % を付ける。不安なら先に jobs -l で PID を確認する。kill %%(最後にアクセスしたジョブ)を使う手もある。

ピンチ 3: バックグラウンドジョブの出力が画面に乱入

症状: & で動かしたコマンドの出力が、他のコマンドを入力している最中に割り込んでくる。

原因: バックグラウンドでも、標準出力と標準エラー出力は既定で端末に流れる。

対処: > file 2>&1 & でファイルへ逃がす。捨ててよいなら > /dev/null 2>&1 & にする。

ピンチ 4: vim を bg したら止まったまま動かない

症状: vimCtrl+Zbg した。しかし jobs で見ると Stopped (tty input) のまま動かない。

原因: vim は端末の入力を待つアプリ。バックグラウンドは端末から切り離された状態なので、キー入力を受け取れない。そのためシェルが自動的に停止状態へ戻す。

対処: vim や top のような対話的アプリは Ctrl+Z で停止 → 用が済んだら fg の往復だけで使う。bg には回さない。

8. 実用テンプレ:ジョブ制御の事故らない型

結論: vim の往復は Ctrl+Z → 別作業 → fg。長い処理は & + > file 2>&1。ログアウトを跨ぐなら nohup / disown / tmux。

コピペ用: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 を忘れると画面が荒れる。この 2 つはセットで覚える。

コピペ用:ログアウト後も継続させる型

# 起動時から 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 で復帰

ミニ課題で手を動かそう

結論: Ctrl+Z と fg の往復、3 ジョブの並行制御、出力をファイルへ逃がす実行の 3 課題で操作を定着させる。

ライニー先輩: 知識だけ入れても定着しない。3 つ手を動かしてみよう。

この課題を試す場所: ジョブ制御はシェルの機能なので、本サイトの仮想ターミナルでは動かない。手元の Linux、WSL、macOS のターミナルで実行しよう。題材は sleep なので、失敗してもファイルは壊れない。

課題 1: Ctrl+Z → fg の往復

やること: sleep 30 を実行しよう。途中で一時停止して別のコマンドを打ち、そのあと元に戻そう。

ヒント1(方向づけ)を見る

「終了」ではなく「一時停止」のキーを使う。止めたあとは、一覧で状態を確かめてから元に戻そう。

ヒント2(コマンド名)を見る

一時停止は Ctrl+Z。状態の確認は jobs。元に戻すのは fg

答えを見る
$ sleep 30
# 5 秒経ったところで Ctrl+Z で一時停止

$ jobs
# → [1]+ Stopped sleep 30 を確認

$ ls    # 別コマンドを打ってみる

$ fg    # sleep に戻って残り時間を待つ

課題 2: 3 つのジョブを並行で動かす

やること: 3 つのコマンドを最初から裏で動かそう。そのうち真ん中の 1 つだけを終了させよう。

ヒント1(方向づけ)を見る

最初から裏で動かす書き方がある。終了させるときは、ジョブの一覧で番号を確かめてから指定しよう。

ヒント2(コマンド名)を見る

裏で動かすのは末尾の &。一覧は jobs。終了は kill %2 のように % を付けて指定する。

答えを見る
$ sleep 100 &
$ sleep 200 &
$ sleep 300 &

$ jobs    # → [1] [2] [3] の 3 つが見えるはず

$ kill %2 # 真ん中だけ終了

$ jobs    # → [2] が消えていることを確認

課題 3: 出力をファイルへ逃がしながら裏で実行

やること: 出力のあるコマンドを裏で動かそう。他のコマンドの出力と混ざらないよう、結果をファイルへためよう。

ヒント1(方向づけ)を見る

裏で動かすだけでは、出力が画面に割り込んでくる。出力の行き先をファイルへ変える書き方を思い出そう。

ヒント2(コマンド名)を見る

行き先の変更は > ファイル名 2>&1。裏で動かすのは末尾の &。ためた内容は cat で見られる。

答えを見る
$ ls -R / > result.log 2>&1 &
[1] 12345

$ jobs             # Running を確認
$ tail result.log  # ファイルへ出力が溜まっていく

# ジョブが終わったら自動的に
# [1]+  Done    ls -R / > result.log 2>&1
# と通知される

2>&1 を付けているので、権限が無くて読めないディレクトリファイルをまとめて整理する入れ物。Windows や macOS の「フォルダ」と同じもの。のエラーもファイル側へ流れる。画面には出てこない。

リナ: できました!Ctrl+Z の安心感、半端ないですね…これさえ知っていれば、vim を Ctrl+C で殺す事故は二度と起きません。
ライニー先輩: それが「ジョブ制御を覚える」という体験だよ。シェルを使う日常が一段階楽になる。

今日の 3 行まとめ

  • Ctrl+Z で一時停止して端末を解放、戻るときは fg、裏で動かすなら bg。これだけで vim を Ctrl+C で殺す事故が消える
  • ジョブ指定には必ず %kill %1kill 1 は別物)。PID と取り違えると init を終了させかねない
  • 長い処理を裏で動かすなら & + > file 2>&1 を癖にする。ログアウトを跨ぐなら nohup / disown / tmux を選ぶ

次に学ぶこと

この記事を共有

次の一手