5.18.2014

Shell: Waiting Until a Condition Becomes Success Within a Time Limit

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 の評価タイミングに注意。

 

References

0 件のコメント:

コメントを投稿