for*_*rin 34 bash sleep timestamps
我正在运行此循环以每秒检查和打印一些内容。然而,因为计算可能需要几百毫秒,所以打印时间有时会跳过一秒。
有没有办法编写这样一个循环,保证我每秒都能得到打印输出?(当然,前提是循环中的计算需要不到一秒钟:))
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
Run Code Online (Sandbox Code Playgroud)
fro*_*utz 67
为了更接近原始代码,我所做的是:
while true; do
sleep 1 &
...your stuff here...
wait # for sleep
done
Run Code Online (Sandbox Code Playgroud)
这稍微改变了语义:如果你的东西用了不到一秒,它会等待整整一秒过去。但是,如果您的东西因任何原因需要超过一秒钟,它就不会继续产生更多的子进程,而且永远不会结束。
所以你的东西永远不会并行运行,而不是在后台运行,所以变量也按预期工作。
请注意,如果您还启动了其他后台任务,则必须将wait
指令更改为仅sleep
专门等待该进程。
如果你需要它更准确,你可能只需要将它同步到系统时钟和 sleep ms 而不是整秒。
如何同步到系统时钟?真的不知道,愚蠢的尝试:
默认:
while sleep 1
do
date +%N
done
Run Code Online (Sandbox Code Playgroud)
输出:003511461 010510925 016081282 021643477 028504349 03...(持续增长)
同步:
while sleep 0.$((1999999999 - 1$(date +%N)))
do
date +%N
done
Run Code Online (Sandbox Code Playgroud)
输出:002648691 001098397 002514348 001293023 001679137 00...(保持不变)
Kus*_*nda 11
在作为后台作业运行的子 shell 中运行操作将使它们不会对sleep
.
while true; do
(
TIME=$(date +%T)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' "$TIME" "$FOO" "$BAR"
) &
sleep 1
done
Run Code Online (Sandbox Code Playgroud)
从一秒中“偷走”的唯一时间是启动子shell 所花费的时间,因此它最终会跳过一秒,但希望比原始代码少。
如果子 shell 中的代码最终使用了超过一秒的时间,循环将开始积累后台作业并最终耗尽资源。
另一种选择(如果您不能使用,例如,watch -p
正如 Maelstrom 所建议的那样)是sleepenh
[ manpage ],它是为此而设计的。
例子:
#!/bin/sh
t=$(sleepenh 0)
while true; do
date +'sec=%s ns=%N'
sleep 0.2
t=$(sleepenh $t 1)
done
Run Code Online (Sandbox Code Playgroud)
请注意,sleep 0.2
模拟执行一些耗时约 200 毫秒的耗时任务。尽管如此,纳秒输出保持稳定(好吧,按照非实时操作系统标准)——它每秒发生一次:
sec=1533663406 ns=840039402
sec=1533663407 ns=840105387
sec=1533663408 ns=840380678
sec=1533663409 ns=840175397
sec=1533663410 ns=840132883
sec=1533663411 ns=840263150
sec=1533663412 ns=840246082
sec=1533663413 ns=840259567
sec=1533663414 ns=840066687
Run Code Online (Sandbox Code Playgroud)
相差不到 1 毫秒,而且没有趋势。这很好;如果系统上有任何负载,您应该期望至少 10 毫秒的反弹——但随着时间的推移仍然没有漂移。也就是说,你不会失去一秒钟。
与zsh
:
n=0
typeset -F SECONDS=0
while true; do
date '+%FT%T.%2N%z'
((++n > SECONDS)) && sleep $((n - SECONDS))
done
Run Code Online (Sandbox Code Playgroud)
如果您的 sleep 不支持浮点秒,则可以使用zsh
'szselect
代替(在 a 之后zmodload zsh/zselect
):
zmodload zsh/zselect
n=0
typeset -F SECONDS=0
while true; do
date '+%FZ%T.%2N%z'
((++n > SECONDS)) && zselect -t $((((n - SECONDS) * 100) | 0))
done
Run Code Online (Sandbox Code Playgroud)
只要循环中的命令运行时间少于一秒,这些就不应该漂移。