timeout コマンド入門 - 実行時間を制限してハングを防ぐ

timeout コマンド入門 - 実行時間を制限してハングを防ぐ

この記事で学べること

  • timeoutコマンドの実行時間に制限 をかけられるようになる
  • 「固まって戻ってこない」コマンドを 自動で打ち切る 方法が分かる
  • タイムアウトしたかどうかを 終了ステータス 124 で判定できる
  • SIGTERM を無視するしぶといプロセスを -k で強制終了 できる

結論(先に覚えるべき型)

  • 時間を区切って実行 → timeout 10 コマンド(10秒で打ち切り)
  • 単位を付けてもよい → timeout 30s / timeout 5m / timeout 1h
  • 打ち切られたかの判定 → 終了ステータスが 124 ならタイムアウト
  • それでも止まらないとき → timeout -k 5 10 コマンド(5秒後に強制終了)

1. timeout コマンドとは何か?

結論: timeout は「指定した時間を超えたらコマンドを自動で終了させる」コマンド。ハング対策の定番。

リナ: 先輩、ダウンロードのコマンドを実行したら、いつまでも終わらなくて画面が固まっちゃいました...。Ctrl+C で止めるしかないんですか?
ライニー先輩: そういうとき便利なのが timeout だよ。「このコマンドは最大 N 秒だけ動かす。それを超えたら自動で止める」 という時間制限をかけられるんだ。
リナ: 自分で Ctrl+C を押さなくても、勝手に止めてくれるんですか?
ライニー先輩: そう。だから「固まったまま放置」を防げる。特に 自動で動くスクリプトや cron では、固まったコマンドが残り続けると大問題になる。timeout はその保険になるんだ。

timeout の基本イメージ

timeout 10 コマンド
        ↑   ↑
        |   実行したいコマンド
        制限時間(秒)

「10秒以内に終われば普通に終了、超えたら強制的に打ち切り」。

2. なぜ timeout が必要なのか?

結論: ネットワーク待ちや無限ループで固まるコマンドを放置すると、スクリプト全体が止まる。timeout がそれを防ぐ。

リナ: でも、ちゃんと動くコマンドなら時間制限なんていらないですよね?
ライニー先輩: 普段はね。問題は 「いつ終わるか分からない」コマンド なんだ。たとえば応答しないサーバーへの接続、消えた相手への ping、書き方を間違えた無限ループ。こういうのは 永遠に戻ってこない ことがある。
リナ: たしかに、さっきのダウンロードがまさにそれでした...。
ライニー先輩: 人間が見ていれば Ctrl+C で止められる。でも cron や自動化スクリプトには 見ている人がいない。固まったプロセスが居座って、次の処理も始まらない。timeout で上限を決めておけば、最悪でも「N秒で諦めて次へ進む」ようにできるんだ。

固まったコマンドを放置すると、メモリやプロセス枠を食いつぶしたり、cron の多重起動を招いたりする。「いつ終わるか分からない処理には時間の上限を付ける」 のが安全な運用。

3. 基本の使い方(時間の指定)

結論: timeout 時間 コマンド の形。数字だけなら秒、s/m/h/d で単位も付けられる。

いちばん基本の形は次の通り。

$ timeout 10 sleep 30

sleep 30 は本来30秒待つコマンド。だが timeout 10 を付けると 10秒で打ち切られる

リナ: sleep 30 なのに10秒で終わった! 約束の30秒より早く止まりましたね。
ライニー先輩: そう、それが時間制限の効果。数字だけ書くと という意味になる。単位を付けて分かりやすく書くこともできるよ。

時間には単位(サフィックス)を付けられる。

$ timeout 30s コマンド    # 30秒
$ timeout 5m コマンド     # 5分
$ timeout 1h コマンド     # 1時間
$ timeout 2d コマンド     # 2日

時間の単位(サフィックス)

書き方 意味
10 10秒(単位なし=秒)
30s 30秒
5m 5分
1h 1時間
2d 2日

0.5 のような小数も使える(timeout 0.5 コマンド で0.5秒)。

4. 終了ステータス 124 でタイムアウトを判定する

結論: timeout で打ち切られたときの終了ステータスは 124echo $?124 ならタイムアウトと判断できる。

timeout が時間切れでコマンドを止めたとき、終了ステータスは 124 になる。これを見ればタイムアウトしたかどうかを機械的に判定できる。

$ timeout 1 sleep 10
$ echo $?
124
リナ: 124 という数字が出ました。これがタイムアウトの目印なんですね。
ライニー先輩: その通り。逆に コマンドが時間内に正常終了すれば、そのコマンド自身の終了ステータス がそのまま返る。たとえば成功なら 0 だね。だから 124 かどうかを見れば「途中で打ち切られたのか、ちゃんと終わったのか」が分かる。

時間内に終わった場合は、コマンド本来のステータスが返る。

$ timeout 10 sleep 1
$ echo $?
0

覚えておきたい終了ステータス

意味
124 タイムアウトで打ち切られた
125 timeout コマンド自体の失敗
126 コマンドは見つかったが実行できない
127 コマンドが見つからない
137 KILL シグナル(-k)で強制終了された(128 + 9)

まずは 「124 = タイムアウト」 だけ覚えれば十分。

5. -k でしぶといプロセスを強制終了する

結論: timeout は既定で SIGTERM を送る。無視されるときは -k 時間 で猶予後に SIGKILL を送って確実に止める。

リナ: timeout で止めたはずなのに、たまにコマンドが止まらないことがあるって聞きました。どうしてですか?
ライニー先輩: いい質問。timeout は時間が来ると、まず SIGTERM(終了のお願い) というシグナルをコマンドに送る。でも、このお願いを 無視するプログラム もあるんだ。そうすると、お願いしても止まらない。
リナ: 「止まってね」とお願いしても聞いてくれないんですね...。じゃあどうすれば?
ライニー先輩: そこで -k(kill-after)を使う。「最初のお願いから N 秒待っても止まらなかったら、今度は強制終了(SIGKILL)する」 という二段構えにできるんだ。
$ timeout -k 5 10 コマンド

これは次の意味になる。

  • まず 10秒 でコマンドに SIGTERM(終了のお願い)を送る
  • それでも さらに5秒 止まらなければ SIGKILL(強制終了)を送る

-k(kill-after)の動き

timeout -k 5 10 コマンド
         ↑  ↑
         |  本来の制限時間(10秒)→ SIGTERM
         追加の猶予(5秒)→ それでも残れば SIGKILL

SIGKILL はプログラム側が拒否できない。確実に止めたいときの最終手段。

SIGKILL(強制終了)は 後始末をする間を与えずに プロセスを止める。書きかけのファイルが壊れる可能性もあるので、まずは普通の timeout(SIGTERM)で試し、止まらないときの保険として -k を使うのがよい。

6. シグナルを指定する(-s)

結論: -s で送るシグナルを変えられる。終了時にコマンド自身のステータスを返したいなら --preserve-status

既定では SIGTERM が送られるが、-s--signal)で別のシグナルを指定できる。

$ timeout -s SIGINT 10 コマンド

これは Ctrl+C と同じ SIGINT を送る。コマンドによっては SIGTERM より行儀よく終われることがある。

リナ: シグナルって、さっきの SIGTERM とか SIGKILL のことですよね。種類があるんですか?
ライニー先輩: たくさんあるよ。timeout でよく使うのは、お願い系の SIGTERM(既定)と SIGINTCtrl+C 相当)、最終手段の SIGKILL。シグナルそのものは少し奥が深いので、別記事(trap 入門)でじっくり扱うね。

タイムアウトしても 124 ではなくコマンド本来の終了ステータスを返してほしい ときは --preserve-status を使う。

$ timeout --preserve-status 10 コマンド

--preserve-status は「タイムアウトかどうかより、コマンドが返した値そのものが知りたい」場面で使う。通常は付けず、124 で判定する方がシンプル。

7. よくある初心者のつまずき

結論: 時間と コマンドの順番、124 の意味、-k の引数の並びを取り違えやすい。

7-1. 時間とコマンドの順番を逆にする

# NG: コマンドが先になっている
$ timeout sleep 30 10

# OK: 時間 → コマンドの順
$ timeout 10 sleep 30

timeout の直後は 必ず時間。その後に実行したいコマンドを書く。順番を逆にすると timeout がエラーになる。

7-2. 124 を「エラー」と勘違いする

リナ: 124 が出たとき、最初は「コマンドが壊れた!」と焦りました。
ライニー先輩: 124「timeout が時間どおり仕事をした」証拠 なんだ。コマンドの不具合ではなく、「時間内に終わらなかったので予定どおり打ち切った」という意味。慌てなくて大丈夫。

7-3. パイプ全体にかけたつもりで一部しか制限されない

# これは「timeout 10 で grep」だけにかかる書き方ではない
$ timeout 10 grep pattern file | sort

timeout 10 grep pattern file の部分にだけ制限がかかり、| sort は別扱いになる。パイプ全体を一括で囲みたいときは、bash -c でまとめる方法がある。

$ timeout 10 bash -c 'grep pattern file | sort'

8. ミニ課題:実際にやってみよう

結論: 打ち切り・終了ステータス確認・強制終了の3問で timeout を手で確かめる。

リナ: 知識は入りました! 手を動かしてみたいです。
ライニー先輩: いいね、3問用意したよ。sleep を題材にすると安全に試せるよ。

課題1: sleep 30 を3秒で打ち切ってみよう。

ヒントを見る

timeout 時間 コマンド の形。数字だけなら秒。

解答例
$ timeout 3 sleep 30

課題2: 課題1の直後に終了ステータスを表示し、124(タイムアウト)になることを確かめよう。

ヒントを見る

直前の結果は $? に入っている。

解答例
$ timeout 3 sleep 30
$ echo $?

124 が表示されればタイムアウト成功。

課題3: sleep 30 を「5秒で SIGTERM、さらに2秒待っても止まらなければ SIGKILL」で止める指定を書いてみよう。

ヒントを見る

-k 猶予秒 制限秒 コマンド の順。

解答例
$ timeout -k 2 5 sleep 30

9. コピペ用テンプレート

結論: 基本・単位付き・判定・強制終了・パイプのよく使う型をまとめて手元に置いておく。

よく使う型をまとめておく

# 基本:10秒で打ち切る
timeout 10 コマンド

# 単位を付ける(秒 / 分 / 時)
timeout 30s コマンド
timeout 5m コマンド

# タイムアウト判定(124 ならタイムアウト)
timeout 10 コマンド
echo $?

# 止まらないときは強制終了(10秒 + 猶予5秒で SIGKILL)
timeout -k 5 10 コマンド

# パイプ全体に制限をかける
timeout 10 bash -c 'コマンドA | コマンドB'

# Ctrl+C 相当(SIGINT)で止める
timeout -s SIGINT 10 コマンド

次に読む