首页 > 解决方案 > 使用内置 printf 以毫秒或纳秒为单位打印当前时间

问题描述

我们可以使用内置函数打印当前时间printf,而无需调用date像这样的外部命令,如下所示:

printf '%(%Y-%m-%d:%H:%M:%S)T %s\n' -1
# sample output: 2019-03-30:17:39:36,846

我们如何使 printf 也打印毫秒或纳秒?在格式字符串中使用%3Nor%N不起作用:

printf '%(%Y-%m-%d:%H:%M:%S,%3N)T %s\n' -1 # outputs 2019-03-30:17:38:16,%3N
printf '%(%Y-%m-%d:%H:%M:%S,%N)T %s\n' -1  # outputs 2019-03-30:17:38:16,%N

但是, date 命令可以正常工作:

date +%Y-%m-%d:%H:%M:%S,%3N # gives 2019-03-30:17:39:36,846
date +%Y-%m-%d:%H:%M:%S,%N  # gives 2019-03-30:17:39:36,160643077

这是在 Red Hat Linux 7.3 版上。

标签: linuxbashdatetimeprintf

解决方案


v5 和$EPOCHREALTIME

  • EPOCHREALTIME微秒粒度的浮点值

  • EPOCHSECONDS自 Unix 纪元以来的秒数

正确的方式

简单地:

IFS=. read ESEC NSEC <<<$EPOCHREALTIME
printf '%(%F:%T)T.%06.0f\n' $ESEC $NSEC

或者,如果您真的不需要存储值:

printf '%(%F:%T)T.%06.0f\n' ${EPOCHREALTIME/./ }

1. 关于$EPOCHREALTIME

请注意:

    EPOCHREALTIME
         Each time this parameter is referenced, it expands to the number
         of seconds since the Unix Epoch  (see  time(3))  as  a  floating
         point  value  with  micro-second  granularity.

因此,如果我在同一行中两次要求相同的变量:

echo $EPOCHREALTIME...  $EPOCHREALTIME 
1572000683.886830... 1572000683.886840

或更清楚地说:

printf "%s\n" ${EPOCHREALTIME#*.} ${EPOCHREALTIME#*.}
761893
761925

echo $((  -10#${EPOCHREALTIME#*.} + 10#${EPOCHREALTIME#*.} ))
37

我的树莓派也一样:

printf "%s\n" ${EPOCHREALTIME#*.} ${EPOCHREALTIME#*.}
801459
801694

echo $((  -10#${EPOCHREALTIME#*.} + 10#${EPOCHREALTIME#*.} ))
246

因此,查询这两次以构建整数部分和小数部分是分开的过程可能会导致问题:(在同一行上,首先访问$ EPOCHREALTIME可能给出:NNN1.999995,然后下一个:NNN2.000002。结果将变为:NNN1.000002带有1000000微秒错误)

2. 警告!关于混合$EPOCHSECONDS$EPOCHREALTIME

将两者一起使用不仅会导致首先提到的错误!

$EPOCHSECONDS使用time()不会不断更新的调用,而$EPOCHREALTIME使用调用gettimeofday()!所以结果可能会有很大差异:

我发现这个对time() 和 gettimeofday() 的答案返回不同的秒数并有很好的解释。

如果我在我的主机上尝试:

epochVariableDiff () {
    local errcnt=0 lasterrcnt v1 v2 v3 us vals line
    while ((errcnt==0)) || ((errcnt>lasterrcnt)); do
        lasterrcnt=$errcnt
        printf -v vals '%(%s)T %s %s' -1 $EPOCHSECONDS $EPOCHREALTIME
        IFS=$' .' read v1 v2 v3 us <<<"$vals"
        [ "$v1" = "$v2" ] && [ "$v2" = "$v3" ] || ((errcnt++))
        [ $errcnt -eq 1 ] && echo "$line"
        printf -v line '%3d %s - %s - %s . %s' $errcnt $v1 $v2 $v3 $us
        printf "%s\r" "$line"
        ((errcnt)) && echo "$line"
        read -t ${1:-.0002}
    done
}


注意:我使用read -t代替sleep,因为sleep不是内置
的 Nota2:您可以使用函数的参数来更改读取超时(睡眠)的值

这可能会导致一些事情:

$ epochVariableDiff .0002
  0 1586851573 - 1586851573 - 1586851573 . 999894
  1 1586851573 - 1586851573 - 1586851574 . 000277
  2 1586851573 - 1586851573 - 1586851574 . 000686
  3 1586851573 - 1586851573 - 1586851574 . 001087
  4 1586851573 - 1586851573 - 1586851574 . 001502
  5 1586851573 - 1586851573 - 1586851574 . 001910
  6 1586851573 - 1586851573 - 1586851574 . 002309
  7 1586851573 - 1586851573 - 1586851574 . 002701
  8 1586851573 - 1586851573 - 1586851574 . 003108
  9 1586851573 - 1586851573 - 1586851574 . 003495
 10 1586851573 - 1586851573 - 1586851574 . 003899
 11 1586851573 - 1586851573 - 1586851574 . 004400
 12 1586851573 - 1586851573 - 1586851574 . 004898
 13 1586851573 - 1586851573 - 1586851574 . 005324
 14 1586851573 - 1586851573 - 1586851574 . 005720
 15 1586851573 - 1586851573 - 1586851574 . 006113
 16 1586851573 - 1586851573 - 1586851574 . 006526
 17 1586851573 - 1586851573 - 1586851574 . 006932
 18 1586851573 - 1586851573 - 1586851574 . 007324
 19 1586851573 - 1586851573 - 1586851574 . 007733
 19 1586851574 - 1586851574 - 1586851574 . 008144

其中整数部分$EPOCHREALTIME可能会增加超过8000微秒之前$EPOCHSECONDS(在我的主机上)。

注意: 这似乎与一些错误有关,重启后不同主机之间或同一主机上的结果可能会有很大差异,以及其他事情......奇怪的是我可以在很多不同的主机上重现它们(英特尔酷睿,英特尔至强, Amd64 ..) 但不是在树莓派上!?(相同的 Debian bash v5.0.3(1)-release),不同的内核版本。

正确:这不是错误!混合time()gettimeofday()一个错误!

所以避免一起使用!

3. 关于printf "..%06.0f"

注意:我使用%06.0f而不是%d确保$NSEC被解释为十进制(浮点数),(如果变量 begin by ,则防止八进制解释0)。

比较:

printf "nn.%06.0f\n" 012345
nn.012345

printf "nn.%06.0f\n" 098765
nn.098765

printf "nn.%d\n" 012345
nn.5349

printf "nn.%d\n" 098765
-bash: printf: 098765: invalid octal number
nn.0

样品运行

小测试:

等到下一秒,然后以微秒打印当前时间

while ! read -t .$((1000000-10#${EPOCHREALTIME#*.})) foo; do
    IFS=. read ESEC NSEC <<< $EPOCHREALTIME
    printf '%(%F:%T)T.%06.0f\n' $ESEC $NSEC
done

您可以按结束此测试Return

2019-10-25:13:16:46.000444
2019-10-25:13:16:47.000489
2019-10-25:13:16:48.000399
2019-10-25:13:16:49.000383
2019-10-25:13:16:50.000508

推荐阅读