while
bash
私のスクリプトには、最初と5秒の間隔で異なる操作を実行するループがあります。前のループの完了を許可します。 5秒間隔は、関数によってdo_different
設定されたグローバル変数で表されますheartbeat
。もう1つの問題は、通常のwhile
ループが未知の時間内に完了することです(RANDOM
以下のスクリプトで簡略化されています)。使用はcron
オプションではなく、ランダムなプロセスタイミングでもありません。
成功せずにパイプとプロセスの置き換えを試してみました。スクリプト全体をリファクタリングできます。
#!/bin/bash
function heartbeat {
do_different=true
while sleep 5s
do
do_different=true
done
}
heartbeat &
while true
do
if $do_different
then
echo 'Something different'
do_different=false
i=0
else
# process of random duration; not important
r=$(( 1 + RANDOM % 3 ))
sleep "${r}s"
i=$((i + r))
echo "$i"
fi
done
答え1
まず、明確でない場合、問題のスクリプトはheartbeat
子プロセスで実行されるため、親シェルメモリのシェル変数を変更できないため、失敗します。
OPが望む精神に近いアプローチは次のとおりです。
#!/bin/bash
trap 'do_different=true' USR1
heartbeat() {
while sleep 5
do # If the parent process has gone away, the child should terminate.
kill -USR1 "$$" || exit
done
}
heartbeat &
heartbeat_pid=$!
trap 'kill "$heartbeat_pid"' 0
do_different=true
while true
do
if "$do_different"
then
echo 'Something different'
do_different=false
i=0
else
# process of random duration; not important
r=$(( 1 + RANDOM % 3 ))
sleep "$r"
i=$((i + r))
echo "$i"
fi
done
変更されたプロセスは、heartbeat
SIGUSR1シグナルを基本(親)シェルプロセスに送信します。このシグナルとSIGUSR2はユーザー/アプリケーションの使用のために予約されており、システムによって生成されてはなりません。このtrap
コマンドを使用すると、シェルスクリプトがシグナルをキャプチャできます。このtrap 'do_different=true' USR1
コマンドはSIGUSR1信号(5秒ごとに到着)をキャプチャし、do_different
信号が発生したときにフラグを設定するようにシェルに指示します。
heartbeat_pid
明らかに、子プロセスのプロセスID(PID)ですheartbeat
。このコマンドは、trap 'kill "$heartbeat_pid"' 0
スクリプトが終了したことを示す偽信号0が「受信」されたときに何が起こるかを定義します。 Shellがドアに「去った後、家に帰る途中で食料品を取ることを忘れないでください」と書かれたステッカーメモを貼るのと同じだと思います。exit
これは、スクリプトが最後に到達したり、ステートメントを実行したり(このスクリプトでは無限ループであるため、両方は発生しません)、割り込み信号(SIGINT、+によって生成されます)によって終了した場合CtrlCheartbeat
答え2
このdate
ユーティリティを使用して、現在時刻を秒単位で取得します。
#!/bin/bash
lastTime=-5
while true
do
currentTime=$(date +%s)
elapsedTime=$((currentTime - lastTime))
if [[ $elapsedTime -ge 5 ]]
then
echo 'Something different'
lastTime=$currentTime
i=0
else
# process of random duration; not important
r=$(( 1 + RANDOM % 3 ))
sleep ${r}s
i=$((i + r))
echo $i
fi
done
編集:lastTime
最初に「他の操作を実行する」ように初期値を変更しました。
答え3
これは私が今実行しているコードです。フェイユの答えいくつかの修正があります。
#!/bin/bash
t_lastdue=$(date --date='5seconds ago' +%s)
while true
do
t_now=$(date +%s)
t_elapsed=$((t_now - t_lastdue))
if [ $t_elapsed -ge 5 ]
then
echo 'Something different'
t_lastdue=$((t_lastdue + 5))
i=0
else
# process of random duration; not important
r=$(( 1 + RANDOM % 3 ))
sleep "${r}s"
i=$((i + r))
echo "$i"
fi
done
答え4
とにかくそれを使用しているので、bash
以下を使用する必要があります$SECONDS
。
#!/bin/bash
while [ "$SECONDS" -lt 5 ] || {
SECONDS=$((i=0))
echo Something different
};do sleep "$((r=(1+RANDOM)%3))"
echo "$((i+=r))"
done
からman bash
:
$SECONDS
- この変数は、シェルが開始されてからの秒数で拡張されます。この変数に割り当てると、カウントは割り当てられた値にリセットされ、拡張値は割り当てられた値に割り当て後の秒数を加えた値になります。
以下の方法そして扱う傾向:
S=SECONDS
while [ "$(($S<5||(i=0*($S-=5))))" -ne 0 ] ||
echo Something different
do sleep "$((r=(1+RANDOM)%3))"
echo "$((i+=r))"
done