使用 printf 在 Bash 中精确时间戳(不是一个小问题)

Ant*_*ber 4 bash timestamps printf

壳牌:猛击。

目标:获取自某个固定时间点以来的时间 t(以毫秒为单位),适用于使用 printf 为内容添加时间戳。

条件:解决方案必须通过多合一文本行测试。

附加:解决方案应该是原子的(是的,对!..),轻量级,将量化和舍入问题保持在最低限度,等等..

t=$[$(date +%s%N)/1000000]<--- 我的解决方案,在这种情况下,固定点是 1970 年 1 月 1 日。但由于两次约会电话,从根本上来说很糟糕。

printf "t=%d\n" $[$(date +%s%N)/1000000] <--- 在这里,使用 printf。

t=$(date +%s)$[10#$(date +%N)/1000000]<--- 可怕的例子。甚至似乎需要取消填充,然后重新填充前导零。

printf "t=%d%03d\n" $(date +%s) $[10#$(date +%N)/1000000] <--- 在这里,使用 printf。

有什么更好的(明智的)建议吗?

编辑(附加):

t=$(date +%s%N)然后printf "%s\n" ${t::13}<--- 我猜,但不是一行。

Sté*_*las 6

正如@Isaac指出,与date实现,支持%N像GNU的或AST开放的,你可以使用%s%3N限制的精度,但除了在ksh93这里date 可以被制成AST-开放的的内置版本date,该date命令不被内置。启动需要几百甚至几千微秒,打印日期和返回需要更多时间。

bash确实复制了ksh93 printf '%(...)T'格式的子集,但不是%N部分。

在这里,您似乎需要使用更高级的 shell,例如ksh93zsh

这些外壳程序可以使它们的$SECONDS变量记录自外壳程序启动以来的时间(并且您也可以重置为任何值)浮点数:

$ typeset -F SECONDS=0; date +%s%3N; echo $SECONDS
1506318780647
0.0017870000
Run Code Online (Sandbox Code Playgroud)

date在这里运行 GNU 最多需要 1787 微秒。

您可以使用$((SECONDS*1000))来获取毫秒数,因为两个 shell 都支持浮点运算(注意ksh93尊重语言环境的小数点)。

对于作为浮点数的纪元时间,zsh$EPOCHREALTIME

$ zmodload zsh/datetime
$ echo $EPOCHREALTIME
1506318947.2758708000
Run Code Online (Sandbox Code Playgroud)

并且ksh93可以使用"$(printf '%(%s.%N)T' now)"(请注意,ksh93的命令替换不会派生进程,也不会为内置函数使用管道,因此不像其他类似 Bourne 的 shell 那样昂贵)。

您还可以在$EPOCHREALTIME那里定义变量:

$ EPOCHREALTIME.get() { .sh.value=$(printf "%(%s.%6N)T");
$ echo "$EPOCHREALTIME"
1506333341.962697
Run Code Online (Sandbox Code Playgroud)

对于自动时间戳,您还可以使用set -o xtrace和 a$PS4打印当前时间。在zsh

$ zsh -c 'PS4="+%D{%s.%.}> "; set -x; sleep 1; date +%s.%N'
+1506332128.753> sleep 1
+1506332129.754> date +%s.%N
1506332129.755322928
Run Code Online (Sandbox Code Playgroud)

在 ksh93 中:

$ ksh -c 'PS4="+\$(printf "%(%s.%3N)T")> "; set -x; sleep 1; date +%s.%N'
+1506332247.844> sleep 1
+1506332248.851> date +%s.%N
1506332248.853111699
Run Code Online (Sandbox Code Playgroud)

根据您的用例,您可以依靠moreutils'sts来标记时间戳:

$ (date +%s.%6N; date +%s.%6N) | ts %.s
1506319395.000080 1506319394.970619
1506319395.000141 1506319394.971972
Run Code Online (Sandbox Code Playgroud)

ts给出它date通过管道从的输出中读取行的时间)。

或者输出行之间的时间:

$ (date +%s.%6N; date +%s.%6N) | ts -i %.s
0.000011 1506319496.806554
0.000071 1506319496.807907
Run Code Online (Sandbox Code Playgroud)

如果您想获得运行给定命令(管道)所需的时间,您还可以使用time关键字,使用$TIMEFORMATin调整格式bash

$ TIMEFORMAT=%E; time date
Mon 25 Sep 09:51:41 BST 2017
0.002
Run Code Online (Sandbox Code Playgroud)

这些时间格式指令最初来自CSH(虽然bash,违背zsh或GNUtime只支持一个小的子集)。在 (t)csh 中,您可以通过设置$time特殊变量来计时每个命令:

$ csh -xc 'set time = (0 %E); sleep 1; sleep 2'
set time = ( 0 %E )
sleep 1
0:01.00
sleep 2
0:02.00
Run Code Online (Sandbox Code Playgroud)

(第一个数字(0此处)表示应该对耗时超过该秒数的命令进行计时,第二个数字指定格式)。


ImH*_*ere 6

由于 bash5+ 有一个简单的解决方案:

$ printf "%.3f\n" $EPOCHREALTIME
1566917328.786
Run Code Online (Sandbox Code Playgroud)

如果不需要点:

printf "%s\n" "$((${EPOCHREALTIME/.}/1000))"
Run Code Online (Sandbox Code Playgroud)

提供自纪元以来的毫秒数,全合一文本行,原子性(对内部 bash 变量的一次调用),轻量级,并且没有量化或舍入问题。