Bash の timeout コマンド

Faaiq Bilal 2024年2月15日
  1. Bash の timeout コマンド
  2. Bash で SIGKILL シグナルとともに timeout を使用する
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
Note
macOS の 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 コマンドの時間単位は秒です。

分、時間、または日で測定された時間値を使用するには、それぞれ mh、または 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 を使用することをお勧めします。