Bash の timeout コマンド
この記事は、Bash の GNU の coreutils
パッケージによる timeout
コマンドを使用して、特定のプログラムのタイムアウトを設定するための簡単なガイドです。
Bash の timeout
コマンド
サーバーからデータをフェッチしたり、ランダムな入力で関数やプログラムを実行したりするなど、多くの状況で、プログラムや関数は非常に長い時間または無期限に実行される場合があります。
このような場合は、プログラムを停止して、時間とリソースが無駄にならないようにすることが不可欠です。
ここでタイムアウト機能が登場します。ユーザーが時間制限を定義すると、プログラムは正常に完了するまで実行できます。ユーザーが定義した制限時間までに時間がかかりすぎると、プログラムは強制終了されます。
通常、タイムアウト機能はほとんどのプログラミング言語に組み込まれています。 ただし、Bash の場合は組み込みではなく、coreutils
と呼ばれる外部パッケージの一部です。
このパッケージは GNU によって開発および保守されており、多くの基本的なユーティリティの実装が含まれています。
coreutils
パッケージは、LINUX システムでコマンド sudo apt-get install -y coreutils
を実行するだけでインストールできます。
macOS システムで coreutils
を取得するには、最初に Homebrew をインストールする必要があります。 Homebrew を取得するには、コマンド ターミナルで以下のコマンドを実行します。
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Homebrew のインストールが完了したら、以下のコマンドを実行します。
brew install coreutils
coreutils
コマンドの場合、すべてのコマンドの名前の前に g
が付きます (例: gdir
または gtimeout
)。coreutils
パッケージがインストールされている場合は、単純に timeout
関数を使用できます。例:
timeout 10 ping google.com
上記のコマンドの出力:
Bash の ping
ユーティリティは組み込みであり、手動で中断するまで実行を続けます。 デフォルトでは、ping
は 1 秒ごとにパッケージを送信します。
上記のように、timeout
コマンドは、timeout
コマンドの直後の数字が制限時間であるため、10 秒後に ping
を強制終了します。
したがって、コマンド timeout 10 cmd1
は、cmd1
が正常終了または 10 秒のいずれか早い方まで実行されることを意味します。 timeout
コマンドの時間単位は秒です。
分、時間、または日で測定された時間値を使用するには、それぞれ m
、h
、または d
を追加します。
たとえば、タイムアウトを 2 分間に設定するには、次のようにします。
timeout 2m programToRun
プロセスがタイムアウトした場合、タイムアウトの終了ステータスは 124
であることに注意してください。 次の例では、これを使用して、プロセスがタイムアウトしたかどうかを確認します。
timeout 1 ping 8.8.8.8 -w3
EXIT_STATUS=$?
if [ $EXIT_STATUS -eq 124 ]
then
echo 'Process timed out!'
else
echo 'Process did not timeout.'
fi
exit $EXIT_STATUS
上記の例では、処理が 1 秒以内に完了しない場合、処理はタイムアウトになります。 -w3
拡張子は、アドレスが 3 回 ping されることを示します。
したがって、このプロセスは常にタイムアウトし、次の出力が得られます。
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=57 time=40.4 ms
Process timed out!
内部タイムアウトも同じ終了ステータス (124
) になることに注意することが重要です。 したがって、この出力は、指定されたタイムアウトを使用してプロセスがタイムアウトしたか、何らかの内部タイムアウトによってプロセスが終了したことを示しています。
Bash で SIGKILL
シグナルとともに timeout
を使用する
通常、timeout
関数は SIGTERM
シグナルを送信して、制限時間に達したときにプログラムの実行を停止します。 問題は、一部のプログラムが SIGTERM
シグナルを無視して実行し続ける可能性があることです。
これが SIGKILL
シグナルの出番です。 プロセスとそのすべての子プロセスの実行を即座に停止し、ブロックまたは無視することはできません。 SIGKILL
でタイムアウトを使用するには、-s
フラグを追加する必要があります。
例えば:
timeout -s SIGKILL 10 programToRun
スクリプトから実行する場合のように、シェルから直接 timeout
を実行しない場合は、timeout
コマンドに --foreground
を追加することが重要です。
timeout
コマンドに代わる多くの方法を使用できますが、主に sleep
で kill シグナルを使用し、必要なコマンドまたはプログラムを実行するという同じロジックに基づいており、sleep
が終了するとすぐに停止します。
例:
(sleep 2 && killall prog) & ./prog
上記のコードは、prog
の実行が完了していない場合、2 秒後に prog
のすべてのプロセスを強制終了します。 このような解決策は機能しますが、timeout
コマンドほど安全で信頼できるものではないため、常に代わりに timeout
を使用することをお勧めします。