bash 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
不是内置的
注2:您可以通过更改函数参数来玩转读取超时值(睡眠)
)
这可能会呈现出类似于:
$ 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
的整数部分可能在$EPOCHSECONDS
之前增加超过8000微秒(在我的主机上)。
注:这似乎与某些错误有关,结果可能因不同的主机或重新启动后在同一台主机上而有很大差异,还有其他事情......奇怪的是,我可以在许多不同的主机(Intel Core、Intel Xeon、Amd64等)上重现这些问题,但在树莓派上却没有!?(相同的Debian bash v5.0.3(1)-release),不同的内核版本。
正确:这不是一个错误!混合使用time()
和gettimeofday()
才是错误!
因此避免同时使用它们!!!
3. 关于printf "..%06.0f"
注:我使用%06.0f
而不是%d
来确保$NSEC
被解释为十进制(浮点数)(如果变量以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 -sn 1 -t .$(( 1000000 - 10#${EPOCHREALTIME#*.} )) _; do
IFS=. read ESEC NSEC <<< $EPOCHREALTIME
printf '%(%F:%T)T.%06.0f\n' $ESEC $NSEC
done
按下任意键即可结束此测试。
2023-01-06:17:28:51.000135
2023-01-06:17:28:52.000095
2023-01-06:17:28:53.000109
2023-01-06:17:28:54.000108
2023-01-06:17:28:55.000132
2023-01-06:17:28:56.000166
2023-01-06:17:28:57.000099
date
命令可以使用。不过,我更喜欢使用内置的printf
命令而不是使用外部命令比如date
。 - codeforesterprintf
命令,因为它(以及用于%(foo)
的底层调用strftime(3)
)不支持这样的功能。 - Shawn/proc/timerlist
。请参见如何在bash中玩弄纳秒!(第二部分,我介绍了高精度睡眠*)。 - F. Hauri - Give Up GitHub