ループ中にbashに影響を与えるためにタイムアウトグローバル変数を使用する方法は?

ループ中にbashに影響を与えるためにタイムアウトグローバル変数を使用する方法は?

whilebash私のスクリプトには、最初と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

変更されたプロセスは、heartbeatSIGUSR1シグナルを基本(親)シェルプロセスに送信します。このシグナルと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

関連情報