Bash命令等待到下一秒



我在问如何等待下一个完整的秒?我不是说睡觉!例如时间是07:45:06.729。最后的729表示毫秒。我怎么能说等到下一秒呢?应该是07:45:07.000

谢谢,)

这就像Matt的回答,但没有一个忙碌的等待,试图早起。这大大简化了,因为我们只需要关心当前时间的小数秒部分。

使用GNU date +%N获取当前时间的纳秒部分,使用sleep获取1 - that

sleep_to_next_second(){
    #TODO: check for 0000 and don't sleep at all, instead of for 0.10000
    sleep 0.$(printf '%04d' $((10000 - 10#$(date +%4N))))
}

(FIXME:要修复特殊情况的错误,您只需对0000进行文本比较。如果是这样,你已经处于第二阶段的开始;不睡觉。因此,将printf结果捕获到local duration=$(...)变量中,或将printf -v duration捕获到变量中。

技巧如下:

  • 带有前导零的数字在bash算术上下文中被视为八进制,因此使用10#$date强制base10。

  • 由于bash只能进行整数数学运算,因此显然应该使用千分之一秒之类的定点,并加上一个前点小数点。这使得前导零必不可少,这样您的睡眠时间为0.092秒,而不是0.92秒。我使用printf '%04d'来格式化我的数字。

实验测试:

# echo instead of sleep to see what numbers we generate
while true;do echo 0.$(printf '%04d' $((10000 - 10#$(date +%4N))));done
...
0.0049
0.0035
0.0015
0.10000    ##### minor bug: sleeps for 0.1 sec if the time was bang-on a ten-thousandth
0.9987
0.9973
0.9960
while true;do date +"%s %N"; sleep 0.$(printf '%04d' $((10000 - 10#$(date +%4N))));done
1445301422 583340443
1445301423 003697512
1445301424 003506054
1445301425 003266620
1445301426 003776955
1445301427 003921242
1445301428 003018890
1445301429 002652820
# So typical accuracy is 0.003 secs after the rollover to the next second, on a near-idle desktop

Ubuntu 15.04: Linux 3.19, Intel i5-2500k,空转1.6GHz, turbo 3.8GHz

GNU sleep命令接受浮点参数。因此,您可以计算等待的确切时间,并将结果调用sleep。

见https://superuser.com/questions/222301/unix-sleep-until-the-specified-time

https://serverfault.com/questions/469247/how-do-i-sleep-for-a-millisecond-in-bash-or-ksh

这更像是一个计时器解决方案。

它需要GNU date(可以处理纳秒)和%N(可以处理小数),sleep(可以处理小数)。

您的里程将在负载较重的系统上有所不同。如果有必要,它会跳过几秒钟。

如果加载系统的精度不是那么重要,那么safety可以更低(或者只是使用Peter的更简单的解决方案)。它越高,所占用的处理器时间就越多,但是在负载过重的系统上,它准时的可能性略高。超过一个点,你只是在浪费cpu时间。即便如此,如果您的系统有更重要的事情要安排,这也可以推迟。

# ms to spend busy waiting * 10
safety=100
while true; do
  if [ "$(date +%s)" != "$last_sec" ]; then
    # Do whatever you need to here
    date +"%S.%N"
    # Then figure out how much to sleep until just 
    # before the next second. 
    sec_ns="$(date +"%s %4N")"
    sec=${sec_ns:0:-5}
    ms=${sec_ns:11}
    last_sec=$sec
    let sleep=10000-10#$ms-$safety
    sleep 0.$(printf '%04d' $sleep)
  fi
done

datesleep的调用是外部二进制文件,因此执行它们会有一些开销。正如在评论中提到的,bash可能不是这项工作的最佳工具。

还要注意,由于使用子字符串,这将在星期六Nov 20 17:46:39 UTC 2286之后失败。希望到那时,我们的电脑霸主们已经从他们的操作系统中删除了bash。谢谢你毁了我的笑话,Peter =)

运行

[vagrant@localhost ~]$ /tmp/wait_seconds.sh 
32.376187825
33.002727030
34.001005707
35.001850686
36.002232134
37.001802235
^C
[vagrant@localhost ~]$ while sleep 0.$(printf '%04d' $((10000 - 10#$(date +%4N) - 1))); do date +"%S.%N"; done
41.006809812
42.005389515
43.005793726
44.004930099
45.004829293
46.005988337

使用bash

sleep echo "scale=3; $((1000 -$(echo $(cut -d '.' -f2 <<< "07:45:06.729"))))/1000"|bc

让我把它分解一下,因为它令人困惑

$(cut -d '.' -f2 <<< "07:45:06.729") #这将获得毫秒部分

构建在上面的命令行

echo "scale=3; $((1000 -$(echo $(cut -d '.' -f2 <<< "07:45:06.729"))))/1000"|bc #这将给你分数

之后,将剩余的传递给sleep编辑:使用更短的bash代码

上面的答案是非常聪明的BASH,但最终我总是遇到奇怪的边缘情况问题,所以我写了一个简单的C程序,每次都能很好地工作,而且完全简单易懂。

// Wait until the next second rolls over
// Use sleep rather than polling

#include <unistd.h>
#include <sys/time.h>

int main(void) {
    unsigned int microseconds;
    struct timeval  tv;
    gettimeofday(&tv, NULL);

    // Calculate time until next round second...
    microseconds = 1000000 - (tv.tv_usec);
    usleep(microseconds);
    return(0);
}
https://github.com/bigjosh/De-Harak-Clock-Controller/blob/master/nextsecond.c

使用命令…构建实用程序

gcc nextsecond.c
chmod +x nextsecond

你可以通过运行…

来测试它是否有效。
./nextsecond; date "+%N"

它将打印自上一轮秒以来的纳秒数,该数应始终小于010000000,即10,000000ns,即10ms。

一个没有任何魔法的简单函数

function sleepUntilTheNextSecond() {
    local currentTimeMilliseconds
    currentTimeMilliseconds=$(date +%s%3N)
    local currentTimeSeconds=$(( currentTimeMilliseconds / 1000 ))
    local targetTimeMilliseconds=$(( (currentTimeSeconds + 1) * 1000 ))
    local sleepMilliseconds=$(( targetTimeMilliseconds - currentTimeMilliseconds ))
    if (( sleepMilliseconds > 1000 )); then
        echo "Invalid sleepMilliseconds (> 1000): $sleepMilliseconds"
        exit 1
    fi
    sleep "0.$sleepMilliseconds"
}

GNU bash, version 4.1.2(2)-release (x86_64-redhat-linux-gnu)测试

最新更新