現在実行中のプログラムをいったん停止して裏で続行させたり、停止していたプログラムを今度は前面で使ったりする。Bashのジョブコントロール機能を使えば簡単だ。
前回はBashのコマンドラインを編集したり、その履歴を管理したりする方法を説明した。今回は、Bashから起動するプログラム=「ジョブ」を制御する方法を説明する。
bash(Linux)には、ユーザーが起動したプログラムを一時的に停止させたり、それをまた再開させたりする機能がある。また単に起動するのではなく、バックグラウンド(裏側)で実行させておき、その間に他のプログラムをフォアグラウンド(表側)でさらに実行させる、といったこともできる。バックグラウンドで動作させたジョブは、表で実行しているジョブ(フォアグラウンドジョブ)に影響を与えることなく、処理を済ませることができる。
このように、プログラムの実行を制御したり、フォアグラウンドやバックグラウンドで実行するように制御する機能のことを「ジョブコントロール(ジョブ制御)」と呼ぶ。
ジョブコントロールを使うと、例えばソースコード編集用のエディタとドキュメント参照用のmanコマンドなどを複数起動しておき、どれか1つだけをフォアグラウンドにして、切り替えながら作業する、といったことが簡単に行える。
GUIのアプリケーションならこんなことは当たり前のようにできるが、単一コンソールを使ったCUIでは、ジョブコントロールでプログラムを切り替えながら作業するのが一般的である。
Linuxのプログラムの基本的な実行単位は「プロセス」である。ユーザーがあるプログラムを起動すると、基本的には1つのプロセスが生成され、実行されることになる。プロセスの実行状況はpsコマンドなどで確認できる。
これに対して「ジョブ」という実行単位もある。ジョブとは通常、Bashなどのシェルで起動した、一連のプログラム群のことを指す。具体的には、1回のコマンドライン入力で起動された、複数のプログラムをまとめてジョブと言う。分かりやすく言えば、OSカーネルが管理する実行単位がプロセスで、シェルが管理する実行単位がジョブである。
1回のコマンドライン入力で1つのコマンド(プログラム)だけを実行すると「1ジョブ=1プロセス」となるが、いくつかのプログラムをまとめて起動すると、「1ジョブ=複数プロセス」となる。
例えば「sleep 10」というコマンドを1つだけ実行するなら(sleepは指定した秒数だけ待つというコマンド。sleep 10なら10秒待ってから終了する。それ以上のことは何もしない)、1ジョブ=1プロセスである。だが「(echo Hello ; sleep 10 ; echo World)」というコマンドなら、1つのジョブが3つのプロセスで構成されていることになる。
もともとUNIXは簡単なプロセスの管理機能しか持っていなかった(プロセスのフォアグラウンドもしくはバックグラウンドでの起動、ストップ、強制終了など)。それが(BSD UNIXの)cshでジョブコントロールの機能が追加され、実行中のプロセスをバックグラウンドへ回したり、その逆にバックグラウンドのジョブをフォアグラウンドへ回したりできるようなった。その後、多くのシェルに取り入れられ、現在では広く使われている。
bashで利用可能なプロセス制御関連のキー割り当てと、ジョブコントロール用コマンドなどを以下にまとめておく。
コマンドやキー割り当て | 機能 |
---|---|
&(コマンド列の末尾に付ける) | バックグラウンドでの実行 |
[Ctrl]+[C]キー | 強制終了。内部的には、SIGINT(Interrupt Signal)を送信している |
[Ctrl]+[\]キー | 強制終了(コアダンプ)。内部的には、SIGQUIT(Quit Signal)を送信している |
[Ctrl]+[Z]キー | サスペンド(一時停止) |
jobsコマンド | ジョブの一覧の表示 |
psコマンド | プロセスの一覧の表示 |
fgコマンド | フォアグラウンドにする |
bgコマンド | バックグラウンドにする |
killコマンド | 終了させる(Signalを送信する) |
suspendコマンド | サスペンド(一時停止)する |
waitコマンド | ジョブの終了を待つ |
disownコマンド | ジョブ一覧からの削除 |
プロセス制御のためのキー割り当て/ジョブコントロール用コマンド 制御文字の割り当ては「stty -a」コマンドで確認/設定できる。 |
以下順に見ていこう。
bashのコンソールから起動したコマンドは、通常はフォアグラウンドのジョブ(プロセス)として実行される。これを強制終了させるには[Ctrl]+[C]キーを押す(Windows OSのコマンドプロンプトと同じだ)。
user01@ubuntu1:~$ sleep 10 ……10秒スリープするコマンド
^C ……[Ctrl]+[C]で強制終了させる
user01@ubuntu1:~$ sleep 10 ……もう1回実行する
^\終了 (コアダンプ) ……[Ctrl]+[Q](SIGQUIT)で強制終了させる。デバッグ用のコアファイル(プロセスの内容をcoreファイルに保存したもの)が出力される点が異なる(設定によっては実際にはcoreファイルは作成されない)
user01@ubuntu1:~$
プロセスやジョブをバックグラウンドで実行させる一番簡単な方法は、コマンド列の最後に「&」記号を付けて実行することである。コマンドをバックグラウンドで起動した場合、フォアグラウンドでは別のコマンドを実行することができるので、そのまま作業を続けていればよい。
user01@ubuntu1:~$ sleep 10 & ……10秒スリープするコマンドをバックグラウンドで実行する
[1] 6711 ……ジョブ番号は1、プロセスIDは6711
user01@ubuntu1:~$ ps ……プロセス一覧の確認
PID TTY TIME CMD
6634 pts/9 00:00:00 bash
6711 pts/9 00:00:00 sleep ……バックグラウンドで動作しているプロセス。プロセスID(PID)が上で表示されているIDと一致している
6712 pts/9 00:00:00 ps
user01@ubuntu1:~$ ……フォアグラウンドはbashの入力待ちになっており、次のコマンドを実行できる。ここでは[Enter]キーを何度か入力してみた
user01@ubuntu1:~$
user01@ubuntu1:~$
[1]+ 終了 sleep 10 ……10秒後にジョブ1が終了した
user01@ubuntu1:~$
コマンドを入力したときに表示される数字はジョブ番号とプロセスIDである。バックグラウンドで実行したジョブが終了すると、次にコマンドを入力したり、[Enter]キーなどを入力したときに「[1+] 終了 <コマンド名>」のように、ジョブ番号と実行が終了したコマンド名が表示される。
ただし、コマンドが何か文字を出力すると、(デフォルトでは)現在のシェルに重なって表示される。また、バックグラウンドのジョブは[Ctrl]+[C]では強制終了できない(止め方は後述。いったんフォアグラウンドに回してからサスペンドしたり、強制終了したりする)。
user01@ubuntu1:~$ ping 10.20.1.101 & ……pingをバックグラウンドで実行させてみる
[1] 6721 ……ジョブ番号1
user01@ubuntu1:~$ PING 10.20.1.101 (10.20.1.101) 56(84) bytes of data. ……コマンドプロンプトが出ているが、pingの出力も重なって表示されている
64 bytes from 10.20.1.101: icmp_seq=1 ttl=128 time=0.672 ms
64 bytes from 10.20.1.101: icmp_seq=2 ttl=128 time=0.247 ms
^C ……[Ctrl]+[C]で止めることはできない
user01@ubuntu1:~$ ^C ……いくら押してもバックグラウンドのジョブは停止できない
user01@ubuntu1:~$ 64 bytes from 10.20.1.101: icmp_seq=3 ttl=128 time=0.226 ms
^C
user01@ubuntu1:~$ ^C
user01@ubuntu1:~$ ^C
user01@ubuntu1:~$ 64 bytes from 10.20.1.101: icmp_seq=4 ttl=128 time=0.238 ms
ls64 bytes from 10.20.1.101: icmp_seq=5 ttl=128 time=0.251 ms 64 bytes from 10.20.1.101: icmp_seq=6 ttl=128 time=0.258 ms
^C
…(以下省略)…
※注意:Windows 10のBash on Ubuntu on Windows上でpingを実行する場合は、bashを管理者権限で起動しないとpingが動作しない
バックグラウンドジョブが文字をフォアグラウンドに出力するのを防ぐには「stty tostop」というコマンドで端末の設定を変更しておけばよい。文字を出力しようとするとジョブが停止状態になる。
user01@ubuntu1:~$ stty tostop ……stty設定を変更する
user01@ubuntu1:~$ ping 10.20.1.101 & ……pingをバックグラウンドで実行させてみる
[2] 6997
user01@ubuntu1:~$ ……[Enter]キーを押してみる
[2]+ 停止 ping 10.20.1.101 ……何も出力せずに停止中になっている
user01@ubuntu1:~$ fg ……fgはフォアグラウンドにするコマンド(後述)
ping 10.20.1.101
PING 10.20.1.101 (10.20.1.101) 56(84) bytes of data. ……表示が開始された
64 bytes from 10.20.1.101: icmp_seq=1 ttl=128 time=0.494 ms
64 bytes from 10.20.1.101: icmp_seq=2 ttl=128 time=0.254 ms
…(以下省略)…
元の設定に戻すには「stty -tostop」とする。
上の例では、文字列を出力するだけのコマンドを実行していたが、もし何らかの入力が必要なコマンドの場合は、入力待ちになった時点でジョブが停止する。バックグラウンドではユーザーの入力を受け取ることができないからだ。
この場合は、後述する方法でフォアグラウンドジョブにしないとプログラムは停止したままである。
user01@ubuntu1:~$ ( sleep 10 ; cat -n ) & ……10秒待ってからユーザー入力を受け付けるジョブ
[1] 6893
user01@ubuntu1:~$ jobs ……ジョブの一覧を確認
[1]+ 実行中 ( sleep 10; cat -n ) & ……実行中のジョブ
user01@ubuntu1:~$
user01@ubuntu1:~$ ……10秒待ってから[Enter]キーを押してみる
[1]+ 停止 ( sleep 10; cat -n ) ……ジョブが停止状態になっている
user01@ubuntu1:~$ fg ……フォアグラウンドで実行させる
( sleep 10; cat -n )
…(以下省略)…
Copyright© Digital Advantage Corp. All Rights Reserved.