シェルスクリプトで時刻の秒が 0 秒になるまで待つ処理を書く
ローカル環境で 1 分ごとに特定の処理を実施する必要が生じたのでシェルスクリプトを書こうと思ったのですが、次のように安易に sleep 60 を行うと 1 分ごとの実行にはなりません。
#!/bin/bash while : ; do (何かの処理) sleep 60 done
例えば、何かの処理、の部分に常に 5 秒かかるとしたら sleep は 60 秒ではなく 55 秒にすべきです。しかし処理にかかる時間がさまざまな外部要因に依存して変動する場合にはこれではダメですね。
こういう場合はスクリプト自身でループや sleep を行わずに単一実行だけ行うようにして、cron で毎分の実行にするのが基本かもしれませんが、1 分ごとに新規にスクリプトが実行されるのはイマイチのように考えました。
そこで sleep の待ち時間を動的に変えるように実装することにしました。どうせなら、計測時のタイムスタンプの秒が 00 になるようにしたかったので、処理の実施後に現在の秒を取得し、そこから sleep の時間を算出するようにしてみました。
#!/bin/bash while : ; do sleepwait=$(( 60 - $( date +"%S" ) )) echo "sleepwait ${sleepwait}" sleep "${sleepwait}" (何かの処理) done
この実装では初回の実行時から処理実施時の秒は 00 になります。また初回の実行は即時の実行ではなく、スクリプト実行開始から 1 分以内に行われます。すぐに実行を開始する必要があり、初回実行時のタイムスタンプで秒が 00 にならなくてもよく、かつ 1 回目と 2 回目の処理が 1 分間隔ではなくてもよいなら次のようにしても良いでしょう。
#!/bin/bash while : ; do (何かの処理) sleepwait=$(( 60 - $( date +"%S" ) )) echo "sleepwait ${sleepwait}" sleep "${sleepwait}" done
なお、いずれの方法でもミリ秒単位の誤差は生じますが実用上の問題はないので、そこはあまり気にしないことにします。macOS と Linux の両方で動作することを確認済み。