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