Linux / コマンド / nohup - ログアウト後もコマンドを実行し続ける

nohupは、端末接続が切断されたときに送信される可能性のあるSIGHUP信号を無視するようにコマンドを実行するツールです。ログアウト後も作業を続けるには、通常nohupとバックグラウンド実行記号&を一緒に使用し、標準入力と出力ファイルも明示的に指定することが推奨されます。

以下のworker.sh・backup.sh・workerは用意済みのスクリプトや実行ファイルに、producer・consumerは実際の処理コマンドに置き換えます。実行権限、必要な依存コマンド、対話入力を要求しないことを先に確認してください。基本形は次の通りです。

nohup ./worker.sh > worker.log 2>&1 < /dev/null &

ただしnohupはプロセスを監視したり再起動するサービスマネージャではありません。再起動後の自動起動、失敗時の再起動、ログローテーションが必要な長期サービスの場合は、systemdによるサービス管理の方が適しています。

nohupはどのようなコマンドですか?

nohupは、指定したコマンドがハングアップ(hangup)信号であるSIGHUPを無視するように設定した後、そのコマンドを実行します。端末を閉じたりSSH接続を終了したりすると、シェルと関連プロセスにSIGHUPが送られることがありますが、これを無視することで作業が続行される可能性が高くなります。

GNU Coreutilsのnohup 公式説明で確認できるように、nohup自体はコマンドをバックグラウンドで実行しません。現在のターミナルで他の作業を続けたい場合は、コマンドの末尾に&を別途付ける必要があります。

構成要素 行うこと 行わないこと
nohup 実行するコマンドがSIGHUPを無視するようにします。 自動的にバックグラウンドで実行しません。
& シェルがコマンドをバックグラウンドジョブとして実行するようにします。 SIGHUPを無視することを保証しません。
出力リダイレクト 標準出力とエラーを指定したログファイルに送ります。 ログサイズの制限や自動削除は行いません。

基本的な文法と推奨実行形態

基本的な文法

nohup の後に実行するコマンドと引数を順に記述します。

nohup command [arg ...]

例えば現在のディレクトリの backup.sh を実行するには、次のように入力します。

nohup ./backup.sh

この形は SIGHUP を無視しますが、フォアグラウンドで実行されます。したがってコマンドが終了するまで、同じシェルで次のコマンドを入力するのは難しいです。

バックグラウンド実行とログ保存

実際の作業では、バックグラウンド実行とログパスを一緒に指定する方が管理しやすいです。

nohup ./backup.sh > backup.log 2>&1 < /dev/null &
  • > backup.logは標準出力を新しいログファイルに書き込みます。同じファイルがある場合、既存の内容を上書きします。
  • 2>&1は標準エラーを現在の標準出力と同じ場所に送ります。
  • < /dev/nullはプログラムがターミナル入力を待たないように、標準入力を、読み取るとすぐにEOF(入力の終わり)になる入力元に変更します。
  • 最後の&はコマンドをバックグラウンドで実行します。

既存のログの後に新しい内容を追記する場合は、>の代わりに>>を使用します。

nohup ./backup.sh >> backup.log 2>&1 < /dev/null &

実行直後のPID保存

バックグラウンドコマンドを実行した直後の$!には、最も最近にバックグラウンドで開始したジョブのプロセスID(PID)が入っています。後で状態を確認したり終了したりする場合、この値を安全な場所に記録しておくと便利です。

nohup ./backup.sh > backup.log 2>&1 < /dev/null &
job_pid=$!
printf '%s\n' "$job_pid" > backup.pid

PIDファイルは、その作業を管理するユーザーのみが書き込めるディレクトリに置いてください。古いPIDファイルが残っていると、同じ番号を後で別のプロセスが使用する可能性があるため、終了前にコマンドと開始時刻も合わせて確認するのが安全です。

標準入力と出力はどのように処理されますか?

標準入力

GNU実装では標準入力が端末に接続されている場合、nohupは標準入力を読み取れない入力元に置き換えます。この動作は端末が消えた後にプログラムが入力を待って停止する状況を減らします。実装の差を減らし意図を明確にするために、コマンドに< /dev/nullを直接記載することもできます。

標準出力とnohup.out

標準出力が端末に向いていて、別のリダイレクトを指定しない場合、GNU nohupは現在のディレクトリのnohup.outに出力を追記します。現在のディレクトリにファイルを作成できない場合は、ユーザーのホームディレクトリにあるnohup.outを試み、両方の場所に書き込みできなければコマンドは実行されません。

複数の作業が同じnohup.outを共有すると、どの作業のログか判別が難しくなり、ファイルもどんどん大きくなる可能性があります。作業ごとに名前と保存方針を決めたログファイルを指定する方が良いです。

標準エラー

標準エラーが端末を向く場合、GNU nohupは通常、標準出力と同じ対象に送られます。しかし、シェル、オペレーティングシステム、先に適用したリダイレクションによって読み手が結果を誤解する可能性があるため、2>&1のように意図を明示するのが安全です。シェルの入出力接続の順序は結果に影響を与えるため、詳細な原理はBashの入出力リダイレクションの説明で確認できます。

実行状態とログを確認する

保存したPIDでプロセスを確認する

実行直後に保存したPIDがある場合、psに直接渡して状態を確認できます。

job_pid=$(cat backup.pid)
ps -p "$job_pid" -o pid,ppid,stat,etime,cmd

PIDはプロセス番号、PPIDは親プロセス番号、STATは状態、ELAPSEDは実行時間です。PIDが存在するという事実だけで望む作業だと断定せず、最後のコマンド列も一緒に確認してください。

名前でプロセスを探す

PIDを保存していない場合は、 pgrep でコマンドラインを検索できます。

pgrep -af 'backup.sh'

同じ文字列を含む他のプロセスも検索される可能性があるため、結果の全コマンドラインを確認する必要があります。単に ps の結果を grep に連結する方法では、検索コマンド自体も表示されやすく、自動化には適していません。

ログをリアルタイムで確認する

tail -f を使用すると、ファイル末尾に追加される内容を継続して確認できます。

tail -f backup.log

ログ監視を終了するには Ctrl+C を押します。これは tail のみを終了し、ログを作成している元の作業は終了しません。tailコマンドの使い方で行数指定とファイル追跡方法をさらに詳しく確認できます。

nohup で開始したプロセスを終了する

まず正常終了を要求する

対象 PID とコマンドを確認した後、kill を実行すると、基本的に SIGTERM シグナルを送ります。プログラムが終了処理コードを持っていれば、ファイルを閉じ、状態を整理する機会を得られます。

job_pid=$(cat backup.pid)
ps -p "$job_pid" -o pid,ppid,stat,etime,cmd
kill "$job_pid"

終了の有無を確認する

少し待ってから同じPIDを再度確認します。

ps -p "$job_pid" -o pid,stat,etime,cmd

シェルスクリプトが複数の子プロセスを作成した場合、親プロセス1つを終了しても子プロセスが残ることがあります。プロセスツリーとプロセスグループを先に確認し、使用しているプログラムが公式に終了コマンドを提供している場合は、それを優先してください。

強制終了は最後の手段として使用する

SIGTERMに反応しないという理由だけで直ちに強制終了すると、データ保存や一時ファイルの整理が完了しないことがあります。原因と対象が正確か確認した上で、最後の手段としてのみSIGKILLを使用します。

kill -KILL "$job_pid"

パイプラインと複合コマンドを実行する

1つのシェルコマンドにまとめる

パイプライン、複数のリダイレクト、条件演算子を1つの作業として実行するには、シェルにコマンド文字列を渡すことができます。

nohup sh -c 'producer | consumer' > pipeline.log 2>&1 < /dev/null &

このとき、引用符内のパイプラインは新しいシェルによって解釈されます。外部からの入力を検証せずにコマンド文字列に結合すると、コマンドインジェクションのリスクが生じるため、信頼できない値を直接挿入しないでください。シェル演算子の実行関係については、Bashコマンド連結演算子の使い方も参考にできます。

ログがすぐに表示されない場合

プロセスは実行中でもログの表示が遅れる場合、nohupよりもプログラムの出力バッファリングが原因である可能性があります。まずプログラム自体のログフラッシュ設定を確認してください。GNU系環境では、プログラムと出力方法が互換性がある場合、stdbufで行単位バッファリングを要求することもできます。

nohup stdbuf -oL -eL ./worker > worker.log 2>&1 < /dev/null &

stdbufはすべてのプログラムに効果があるわけではありません。プログラムが自分でバッファを再設定したり、特定の入出力方式を使用する場合は適用されないことがあります。

nohupの限界と他のツールを選ぶ基準

nohupが提供しない機能

nohupは、1つのシグナルの処理方法を変更する簡単な実行ツールです。次の機能は提供されません。

  • システム再起動後の自動起動
  • プロセスが異常終了したときの自動再起動
  • 依存サービスの起動順序管理
  • CPU・メモリなどの資源制限と状態監視
  • ログファイルのサイズ制限、圧縮、保管および削除
  • 対話型ターミナルセッションの保持

systemd、tmux、screenとの違い

ツール 適した状況 特徴
nohup 短時間実行する非対話型バッチ作業 簡単ですが、監視・再起動・ログローテーション機能はありません。
systemd サーバーで継続的に運用するサービス ブート連動、再起動ポリシー、依存関係、ログおよびリソース管理を構成できます。
tmux·screen 後で再度接続する必要がある対話型作業 仮想ターミナルセッションを切り離して再接続できます。
タスクスケジューラー 決められた時刻や周期で実行する作業 cronや専用作業キューで実行時点と履歴を管理します。

disownとの違い

disownは、Bashのような一部のシェルが、すでに開始したジョブをシェルのジョブリストから削除したり、ハングアップシグナルの処理方法を変更するシェル組み込み機能です。一方、nohupは、コマンドを開始するときからSIGHUPを無視する外部プログラムです。disownのオプションと動作はシェルによって異なるため、移植可能なスクリプトでは現在のシェルのドキュメントを確認する必要があります。

安全に実行するためのチェックリスト

  • パス: スクリプト、設定ファイル、ログファイルは可能な限り絶対パスで指定します。ログインした場所が異なっても同じファイルを使用できるようにします。
  • 権限: ログにパスワード、トークン、個人情報が残る可能性があるか確認し、ファイルの権限を制限します。
  • 容量: ログが増え続けないようにlogrotateやプログラム自体のログ保持ポリシーを構成します。
  • 重複実行: 同じ作業が同時に複数回実行されても安全か確認します。必要に応じてロックファイルやプログラムの単一実行機能を使用します。
  • 環境: 非対話型実行では作業ディレクトリ、PATH、ロケール、環境変数がログインシェルと異なる場合があるため、必要な値を明示します。
  • 機密引数: パスワードやトークンをコマンドライン引数として渡すと、他のユーザーがプロセスリストで見ることができます。プログラムがサポートする認証ファイルや秘密の保存方法を使用してください。
  • 終了手順: 実行直後にPIDを記録し、正常終了方法と残りの子プロセス確認手順を準備します。

よくある質問

SSHウィンドウを閉じるとnohupジョブは必ず維持されますか?

nohupで始めたコマンドはSIGHUPを無視しますが、すべての環境で作業の存続を絶対的に保証するわけではありません。プログラムが別の子プロセスを作成してシグナル設定を変更したり、端末装置を継続使用する場合は結果が異なる可能性があります。標準入出力をすべて端末から切り離します。別の終了シグナルやログインセッション全体を終了するポリシーから保護する機能ではないため、重要な長時間作業はサービスマネージャで管理する方が適しています。

nohup.outファイルを作らないようにするにはどうすればいいですか?

標準出力と標準エラーを直接指定すればよいです。ログが全く必要ない作業だけ/dev/nullに送ることができますが、問題を診断する情報も失われるため、運用作業には通常、別のログファイルを推奨します。

nohup ./worker > /dev/null 2>&1 < /dev/null &

jobsで作業が表示されない理由は何ですか?

jobsは、現在シェルが管理している作業しか表示しません。ログアウトして新しいシェルに接続すると、前のシェルの作業一覧は引き継がれないためjobsには表示されません。保存したPIDとpsを使うか、サービス管理ツールの状態確認機能を使用してください。

nohupの終了コードは何を意味していますか?

GNU実装では、nohup自体のエラー、コマンドが見つからない場合、コマンドを実行できない場合に別の終了コードを使用し、正常にコマンドを開始した場合はそのコマンドの終了状態を返します。正確な値は実装やPOSIXLY_CORRECTの設定の影響を受ける可能性があるため、自動化では該当システムのnohup --helpと公式ドキュメントを確認してください。バックグラウンド起動直後の$?は、そのジョブの最終結果を表しません。同じ親シェル内で待てる場合はwait "$job_pid"の終了状態を記録できます。ログインし直した新しいシェルでは、以前の子プロセスをそのままwaitできません。

まとめ

nohupは、ログアウトやターミナル終了時に発生する可能性のあるSIGHUPを無視するようにコマンドを実行します。一般的な非対話型作業ではnohup、&、および明示的な標準入出力リダイレクションを組み合わせて使用し、実行直後にPIDを保存すると管理が容易です。

作業が再起動後にも開始される必要がある場合や、失敗時に自動回復が必要な場合は、nohupの代わりにsystemdのようなサービスマネージャを選択してください。その他の基本的なツールを一目で確認したい場合は、Linuxでよく使われるコマンド一覧を参照できます。