Bourne Shell: 指定されたコマンドを一定間隔で実行し、正常終了するまで待つ (タイムアウトあり)
これぞ車輪の再発明という感があるが、探しても見つからなかったので書く。
目的
以下のような処理を行う、汎用的なシェルスクリプトを作成したい
- パラメータとして、タイムアウト時間(秒)、実行間隔(秒)、コマンド(引数付きでも可) を指定
- パラメータとして渡されたコマンドを実行
- コマンドのリターンコードが 0 である場合、すぐに処理を終了
リターンコード 0 を返す - コマンドのリターンコードが 0 以外である場合、指定された実行間隔だけ待機し
その後再度コマンドを実行。これをリターンコードが 0 になるまで繰り返す - シェルスクリプトを実行してから、コマンドのリターンコードが 0 になることのないまま
タイムアウト時間が経過した場合、リターンコード 1 を返して終了
たとえば、やりたいのはこんな処理。
コード
GitHub にアップ。
-h オプションでヘルプ表示
NAME await-until -- Await until the specified command becomes success SYNOPSIS await-until [-t TIMEOUT] [-i INTERVAL] [-vh] COMMAND DESCRIPTION The options are as follows: -t TIMEOUT Specify a timeout in seconds. The default value is $TIMEOUT. -i INTERVAL Wait INTERVAL seconds between executing each COMMAND. The default value is $INTERVAL. -v Print verbose messages. The default is 'disabled'. -h Print this help message. COMMAND Command line for testing. This string will be evaluated with 'eval' command in '/bin/sh'. EXIT STATUS This command exits with one of following values: 0 COMMAND returns 0 within TIMEOUT seconds. 1 Timed out. >1 An error occurred.
-v オプションは主にデバッグ用。コマンドは Bourne Shell の eval で評価される。
使用例
- 常に成功するコマンド
$ await-until -v true [2014-05-18T06:44:38+0900] cmd: true => rc: 0 [2014-05-18T06:44:38+0900] Normal end. $ echo $? 0
コマンドはすぐに終了する
- 常に失敗するコマンド
$ await-until -v false [2014-05-18T06:48:30+0900] cmd: false => rc: 1 [2014-05-18T06:48:31+0900] cmd: false => rc: 1 [2014-05-18T06:48:33+0900] cmd: false => rc: 1 [2014-05-18T06:48:34+0900] cmd: false => rc: 1 [2014-05-18T06:48:35+0900] cmd: false => rc: 1 [2014-05-18T06:48:36+0900] cmd: false => rc: 1 [2014-05-18T06:48:37+0900] cmd: false => rc: 1 [2014-05-18T06:48:38+0900] cmd: false => rc: 1 [2014-05-18T06:48:39+0900] cmd: false => rc: 1 [2014-05-18T06:48:40+0900] cmd: false => rc: 1 [2014-05-18T06:48:41+0900] Timed out. $ echo $? 1
タイムアウト時間になるまでコマンドが試行され、最終的にリターンコード 1 を返す。
- 途中で成功に変わるコマンド
$ await-until -v '((1`date +%S` % 10 == 0))' [2014-05-18T06:51:03+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:04+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:05+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:06+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:07+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:08+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:09+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 1 [2014-05-18T06:51:10+0900] cmd: ((1`date +%S` % 10 == 0)) => rc: 0 [2014-05-18T06:51:10+0900] Normal end. $ echo $? 0
システム時刻の秒数の下1桁が0になるまで待つ処理。
毎回 date コマンドが評価されているのがわかる。秒数の前に「1」を付けているのは、「08秒」、「09秒」の時に 8進数表記と見なされてエラーになる現象を回避するため。
- コマンドの途中で Ctrl+C
$ await-until -v false [2014-05-18T06:52:28+0900] cmd: false => rc: 1 [2014-05-18T06:52:29+0900] cmd: false => rc: 1 [2014-05-18T06:52:30+0900] cmd: false => rc: 1 ^C[2014-05-18T06:52:31+0900] Interrupted. $ echo $? 3
sleep などの子プロセスは全て即座に終了するので、プロセスのゴミは残らない。
- Redis の BGSAVE の終了待ちは、以下のように短く書けるようになった。
last_save=`redis-cli LASTSAVE` redis-cli BGSAVE await-until '[[ '$last_save' != `redis-cli LASTSAVE` ]]' if [ $? -ne 0 ]; then # エラー処理 fi
$last_save の評価タイミングに注意。
0 件のコメント:
コメントを投稿