如何使用定时全局变量影响 bash while 循环?

如何使用定时全局变量影响 bash while 循环?

while我的脚本中有一个循环bash,它应该在开始时和每 5 秒的时间间隔执行不同的操作。允许完成任何先前的循环。 5s间隔由函数do_different设置的全局变量表示heartbeat。另一个复杂之处是正常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在子进程中运行,因此无法更改父 shell 内存中的 shell 变量。

这是一种更接近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向主(父)shell 进程发送 SIGUSR1 信号。该信号和 SIGUSR2 保留供用户/应用程序使用(并且不应由系统生成)。该trap命令允许 shell 脚本捕获信号。该trap 'do_different=true' USR1命令告诉 shell 捕获 SIGUSR1 信号(每五秒到达一次)并do_different在该信号发生时设置标志。

heartbeat_pid显然,是子进程的进程ID(PID)heartbeat。该命令trap 'kill "$heartbeat_pid"' 0定义了“收到”伪信号 0 时发生的操作,该伪信号表示脚本退出。可以将其想象为贝壳在门上贴了一张便利贴,上面写着“离开后,记得在回家的路上购买杂货”。如果脚本到达末尾或执行语句(这两种情况都不会发生在该脚本中,因为它是一个无限循环),或者如果它被中断信号(SIGINT,由+exit生成)终止,则将调用此操作)。这是一个安全网;该进程已被编写为在父进程消失时终止。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

  • 该变量扩展到 shell 启动后的秒数。对此变量的赋值会将计数重置为分配的值,并且扩展值变为分配的值加上自分配以来的秒数。

方法如下处理漂移:

S=SECONDS
while   [ "$(($S<5||(i=0*($S-=5))))" -ne 0 ] ||
        echo Something different
do      sleep "$((r=(1+RANDOM)%3))"
        echo  "$((i+=r))"
done

相关内容