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 を押さなくても、勝手に止めてくれるんですか?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 で打ち切られたときの終了ステータスは
124。echo $?が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秒)→ それでも残れば SIGKILLSIGKILL はプログラム側が拒否できない。確実に止めたいときの最終手段。
SIGKILL(強制終了)は 後始末をする間を与えずに プロセスを止める。書きかけのファイルが壊れる可能性もあるので、まずは普通の timeout(SIGTERM)で試し、止まらないときの保険として -k を使うのがよい。
6. シグナルを指定する(-s)
結論:
-sで送るシグナルを変えられる。終了時にコマンド自身のステータスを返したいなら--preserve-status。
既定では SIGTERM が送られるが、-s(--signal)で別のシグナルを指定できる。
$ timeout -s SIGINT 10 コマンド
これは Ctrl+C と同じ SIGINT を送る。コマンドによっては SIGTERM より行儀よく終われることがある。
timeout でよく使うのは、お願い系の SIGTERM(既定)と SIGINT(Ctrl+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 を手で確かめる。
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 コマンド