シェルを閉じてもコマンドを実行させ続けるために nohup を使う
先週末都内に用事があったので、まだ一度も見たことがない隅田川花火大会、行くぞ~(後半から)と思っていたら雷雨に遭い花火が見られなかった、tanakaです。
今日は時間のかかるコマンドを実行させるときに便利な nohup を紹介します。
それなりのデータ量を扱うアプリケーションを運用していると時間のかかるシェルタスクを実行しなければならないこともよくあります。そんなとき便利なのがnohupです。つい最近までこのコマンドを知らなかったのですが、もし必要があればGNU Screenでデタッチすれば同じことが実現できていました。しかしGNU Screenがない環境で実行する必要があり、調査して見つけたのがnohupです。
時間のかかる処理のサンプル
まずはnohup を使わないでプログラムを実行してみます。以下は1秒ごとに標準出力に文字列を出力し、20秒で処理が終わるプログラムです
nohup_test.php
<?php
$stdout = fopen('php://stdout', 'w');
for ($i = 1; $i <= 20; $i++) {
fwrite($stdout, "{$i}秒経過" . PHP_EOL);
fflush($stdout);
sleep(1);
}
fclose($stdout);
単純に実行してみます。
$ php ./nohup_test.php
1秒経過
2秒経過
3秒経過
...
20秒経過
バックグラウンドジョブで実行
さて、時間がかかる間に他のタスクをしたいので、バックグラウンドで実行させることにします。
$ php ./nohup_test.php > batchout.log 2> batcherr.log &
[1] 663
$
[1] + done php ./nohup_test.php > batchout.log 2> batcherr.log
最後に& をつけてバックグラウンドジョブにするとプロンプトがすぐ表示され他の仕事ができます。ジョブの完了は20秒後に通知されます。
バックグラウンドジョブ、便利ですが、気をつけないといけないことがあり、このジョブを実行したシェルを閉じるとジョブも強制的に途中でも終了してしまいます。
% php ./nohup_test.php > batchout.log 2> batcherr.log &
[1] 697
[~] <ttys001>
% exit
zsh: you have running jobs.
上記の実行結果ではまだ実行中のジョブがあると警告を受けましたが、そのままもう一度exitを実行して、ログアウトしてみます。そうしてから、再ログインしてログを確認してみると、PHPのプログラムが最後まで実行されずに終了してしまったことがわかります。これは実行させたジョブがログインしたシェルプロセスに属していて、ログアウト時には終了させるようなシグナルを送っているからだそうです。(このシグナルについては次の節で)これでは勤務時間内に終わらない処理には使えませんね。
nohup を使いログアウトしてもコマンド実行を継続する
nohup を使うとログアウト時に送られていたシグナル(ハングアップシグナル)を無視するようにプログラムを起動します。以下のように実行します。
$ nohup php ./nohup_test.php < /dev/null > batchout.log 2> batcherr.log &
[1] 791
まとめ
- 単純に実行したバックグラウンドジョブは、ログアウトすると実行途中でも終了してしまいます
- nohupを使うとハングアップシグナルを無視してプログラムを起動します。最後に"&"をつけてバックグラウンドジョブにすれば、シェルをログアウトしてもプログラムを実行させ続けることができます。