私はbashスクリプトに初めて触れ、いくつかのサンプルスクリプトで始めます。
一つは:
#!/bin/bash
SECONDS=5
i=1
while true
do
echo "`date`: Loop $i"
i=$(( $i+1 ))
sleep $SECONDS
done
その結果は次のとおりです。
Sunday 10 May 15:08:20 AEST 2020: Loop 1
Sunday 10 May 15:08:25 AEST 2020: Loop 2
Sunday 10 May 15:08:35 AEST 2020: Loop 3
Sunday 10 May 15:08:55 AEST 2020: Loop 4
...これは私が期待したりスクリプトで実行したいのではありません。
ループを通過するたびに秒数が2倍になるのはなぜですか?
bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
答え1
SECONDS
bashの内部変数だからです。シェルが実行された時間を保存します。
からman bash
:
SECONDS
このパラメータが参照されるたびにシェルが呼び出されてからの秒数が返されます。値がSECONDSに割り当てられている場合、後続の参照によって返される値は、割り当て後の秒数に割り当てられた値を加算した値です。
したがって、スクリプトは次のように動作します。
- 最初に使用される場合、値5は入力として受け入れられ、初期値は5です。
- その後、5秒の睡眠があり、睡眠が返されると、SECONDSの値は初期値の5秒+ 5秒の睡眠= 10秒になります。
- 次に、10秒(現在の値SECONDS)に前の10秒の累積時間である20を加えて睡眠をとります。
- 20秒間睡眠を繰り返し、前の値である20を40に追加します。
- など。
答え2
この構成には2次問題があります。ループの処理に時間がかかる場合(たとえば、1.4秒)、ループは6.4秒ごとに繰り返されます。
これが重要な場合は、代わりにサイクル数を計算できます。
#!/bin/bash
Expired=$(( SECONDS + 23 ))
Tick=5
Iter=1
while (( SECONDS < Expired )); do
echo "$(date '+%T.%N'): Loop $Iter"
sleep 1.4 #.. Simulate work.
echo "$(date '+%T.%N'): Idle"
sleep $(( Iter++ * Tick - SECONDS ))
done
サイクル時間は、秒単位の整数精度内で安定しています。
Expiredを初期化するために、より複雑な算術を実行できます(たとえば、2つの日付コマンドを使用して真夜中までの秒数を見つけます)。