シェルスクリプトでコマンドを制限時間(タイムアウト)付きで実行させる
はじめに
コマンドやシェルスクリプトを制限時間付きで実行させ、制限時間を超えたらタイムアウトさせたい場合があります。
例えば、特定のファイルが作成されるまで待ち続けますが、そのファイルを作成する別プロセスがエラー終了してしまい永遠に待ち続けてしまう、といったようなケースに利用できます。
検証環境
$ uname -moi
x86_64 MacBookPro16,1 Darwin
$ bash -version | head -n 1
GNU bash, バージョン 5.1.4(1)-release (x86_64-apple-darwin20.2.0)
$ timeout --version | head -n 2
timeout (GNU coreutils) 8.22
Copyright (C) 2013 Free Software Foundation, Inc.
timeout
コマンドを利用する
timeout
コマンドを利用することでコマンド実行にタイムアウトの設定をすることができます。
timeout
コマンドを通して sleep
コマンドを実行し、挙動を見ていきましょう。
制限時間(3 秒)内に終了する場合
timeout
コマンドは、第 1 引数に制限時間(デフォルトの単位は秒)、第 2 引数に実行するコマンドを指定します。
3 秒の制限時間を指定する場合には、 timeout 3 <COMMAND>
のように指定します。
ここでは、 sleep 2
コマンドを実行し、2 秒間だけ待った後終了するコマンドを実行させてみます。
$ timeout 3 sleep 2
$ echo $?
0
timeout
コマンドの制限時間に引っかからずにコマンドが終了した場合には、実行後ステータスを示す変数 $?
は 0 (正常終了) となります。
制限時間(3 秒)外に終了する場合
次に、 sleep 4
コマンドを実行し、4 秒間だけ待った後終了するコマンドを実行させてみます。
3 秒の制限時間に引っかかるはずです。
$ timeout 3 sleep 4
$ echo $?
124
timeout
コマンドの制限時間に引っかってコマンドが終了した場合には、実行後ステータスを示す変数 $?
は 124 となります。
timeout
コマンドはどうやって実行コマンドを停止させている?
timeout
コマンドの第 2 引数に指定する「実行コマンド」ですが、どうやって停止させているのでしょうか?
これは、 SIGTERM
シグナルを送信して停止しています。シグナルについては以下のエントリでも取り扱っているのでご参考ください。
本当に SIGTERM
シグナルが送られているのかを確認してみます。
以下のようなスクリプト( test.sh
)を作成します。
#!/usr/bin/env bash
set -o errexit
trap 'echo start sigterm trap; sleep 5; echo end sigterm trap' SIGTERM
echo start
sleep 5
echo end
実行すると以下のような結果が得られます。 start の文字列が出力された後、5 秒待って end の文字列が出力されます。
$ ./test.sh
start
end
こちらのスクリプトを timeout
コマンドを使って 3 秒タイムアウトを設定して実行します。
$ timeout 3 ./test.sh
start
Terminated
start sigterm trap
end sigterm trap
start が出力されてから 3 秒後にタイムアウトとなり、 ./test.sh
に対して SIGTERM
シグナルが送られます。 ( なぜこんな面倒なことをしているかは後述 )./test.sh
では、 SIGTERM
シグナルをトラップして追加の処理を行うようにしています。
start sigterm trap
と出力- 5 秒待つ
end sigterm trap
と出力
想定通りの挙動を確認できました。
SIGTERM
シグナル送信後の終了処理にもタイムアウトを設定したい
先の ./test.sh
のケースでは、 SIGTERM
シグナル送信後も 5 秒待たされてしまいます。SIGTERM
シグナル送信後の処理についても制限時間を設定したい場合には -k
オプションを付与します。
以下のコマンドでは、 ./test.sh
スクリプトの終了を 最長 3 秒 待ちます。超過時は SIGTERM
シグナルを送信し終了させます。
さらに、 SIGTERM
シグナル送信後に 最長 2 秒 待ちますが、超過時には SIGKILL
シグナルを送信し強制終了させます。
$ timeout -k 2 3 ./test.sh
start
Terminated
start sigterm trap
Killed
ひとこと
こちらも上手に利用すれば、うまいこと処理がかけそうな気もするのですが、今の所業務コードで利用したことはありません。curl
にもタイムアウトオプションがありますし、明確なタイムアウトを設定するようなロジックはあまりシェルでは書かないですね。他のプログラミング言語を使うことが多いでしょう。
ディスカッション
コメント一覧
まだ、コメントがありません