我正在运行这个循环来每秒检查和打印一些东西。然而,由于计算可能需要几百毫秒,打印时间有时会跳过一秒钟。
有什么办法可以编写这样一个循环,保证我每秒都能得到一个打印输出?(当然,前提是循环中的计算时间少于一秒 :))
while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' $TIME $FOO $BAR
sleep 1
done
为了更接近原始代码,我所做的是:
这稍微改变了语义:如果你的东西用了不到一秒钟,它只会等待整整一秒钟过去。但是,如果您的东西由于任何原因花费的时间超过一秒钟,它就不会继续产生更多的子进程,而且永远不会结束。
所以你的东西永远不会并行运行,而不是在后台运行,所以变量也可以按预期工作。
请注意,如果您还启动了其他后台任务,则必须将
wait
指令更改为仅专门等待sleep
进程。如果您需要它更准确,您可能只需将其同步到系统时钟并休眠毫秒而不是整秒。
如何同步到系统时钟?真的不知道,愚蠢的尝试:
默认:
输出:003511461 010510925 016081282 021643477 028504349 03...(不断增长)
同步:
输出:002648691 001098397 002514348 001293023 001679137 00...(保持不变)
如果您可以将循环重组为脚本/oneliner,那么最简单的方法是使用
watch
及其precise
选项。你可以看到效果
watch -n 1 sleep 0.5
- 它会显示秒数,但偶尔会跳过一秒。运行它将watch -n 1 -p sleep 0.5
每秒输出两次,每秒,你不会看到任何跳过。在作为后台作业运行的子 shell 中运行操作将使它们不会对
sleep
.从一秒中“偷走”的唯一时间是启动子外壳所花费的时间,因此它最终会跳过一秒,但希望比原始代码少。
如果 subshell 中的代码最终使用超过一秒,则循环将开始累积后台作业并最终耗尽资源。
另一种选择(如果您不能使用,例如,
watch -p
如 Maelstrom 建议的那样)是sleepenh
[ manpage ],它就是为此而设计的。例子:
请注意,
sleep 0.2
在那里模拟执行一些耗时约 200 毫秒的任务。尽管如此,纳秒输出保持稳定(嗯,按照非实时操作系统标准)——它每秒发生一次:相差不到 1 毫秒,而且没有趋势。这很好;如果系统上有任何负载,您应该期望至少有 10 毫秒的反弹——但随着时间的推移仍然没有漂移。即,你不会失去一秒钟。
与
zsh
:如果你的 sleep 不支持浮点秒,你可以使用
zsh
'szselect
代替(在 a 之后zmodload zsh/zselect
):只要循环中的命令运行时间不到一秒钟,这些就不会漂移。
我对 POSIX shell 脚本有完全相同的要求,其中所有帮助程序(usleep、GNUsleep、sleepenh,...)都不可用。
见:https ://stackoverflow.com/a/54494216