gnuのcoreutilsの中にtimeoutというコマンドがある。
これを使うと、「コマンドが一定時間たっても終了しない場合にkillする」という処理が実現できる。
詳しくはこちらをご覧いただきたい。
https://qiita.com/ma2saka/items/741b614418bf8fce04de
通常の環境ではcoreutilsのtimeoutを用いればよい。
ここでは「coreutilsが使えない環境でシェルスクリプトで実現したい」というマニアックな要望を叶えるための方法について記載する。
参考にした記事:
- https://unix.stackexchange.com/questions/43340/how-to-introduce-timeout-for-shell-scripting
- https://qiita.com/dogyear/items/e58ddab9a49bf82ed43f
- https://stackoverflow.com/questions/3299502/how-to-return-spawned-process-exit-code-in-expect-script
結論
こちらを.bashrcなり.zshrcなりに書けば終わり。
timeout() {
time=$1
# start the command in a subshell to avoid problem with pipes
# (spawn accepts one command)
command="/bin/sh -c \"${@:2}\""
expect -c "set timeout $time; spawn -noecho $command; expect timeout { exit 124 } eof; catch wait result; exit [lindex \$result 3]"
}
使い方
timeout 1 sleep 3 # `sleep 3`が途中でkillされて、1秒で処理が完了する。timeoutした場合の返り値は124
内容の説明
中でexpectというコマンドを使っている。expectは本来は対話式のコマンドを自動化するためのコマンド。しかしこの中にtimeoutの機能も含まれているのでそれを利用している。
expectの引数で与えるコマンドの中で set timeout {秒数}
とするとタイムアウトの時間を設定できる。
spawn -noecho $command;
の spawnというのは外部プロセスを起動するexpectのスクリプトの記法。ここでコマンドを起動している。
expect timeout { exit 124 } eof
はパターンマッチ的な処理を行なっている。もしタイムアウトによって処理が終了した場合は、リターンコード124を返す。そうではない場合は eofの処理が行われるが、ここでは特別に何かを行なっているわけではない。
catch wait result; exit [lindex \$result 3]
ここがspawnで起動したコマンドのリターンコードを取得している箇所。resultという変数に結果が入っている。resultは配列になっていて、プロセスの返り値以外の情報も入っている。lindexで返り値のみを取得する。\$result
というようにエスケープされているのは、変数名がシェルではなくexpectコマンドによって解釈されるようにするため。