外壳:重击。
目标:获取自某个固定时间点以来的时间 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}
<---我猜,但不是一行。
答案1
从 bash5+ 开始有一个简单的解决方案:
$ printf "%.3f\n" $EPOCHREALTIME
1566917328.786
如果不需要该点:
printf "%s\n" "$((${EPOCHREALTIME/.}/1000))"
提供自纪元以来的毫秒时间,全在一行文本中,原子(对内部 bash 变量的一次调用),轻量级,并且没有量化或舍入问题。
答案2
正如 @Isaac 所指出的,通过支持GNU 或 ast-open 等date
的实现,您可以使用它来限制精度,但除了在哪里%N
%s%3N
ksh93
date
能被制作为 ast-open 的内置版本date
,该date
命令不是内置的。启动需要几百微秒甚至几千微秒,打印日期和返回还需要更多时间。
bash
确实复制了格式的子集ksh93
printf '%(...)T'
,但没有复制%N
部分。
在这里,您似乎需要使用更高级的 shell,例如ksh93
或zsh
。
这些 shell 可以使它们的$SECONDS
变量记录自 shell 启动以来的时间(并且您也可以重置为任何值)浮点:
$ typeset -F SECONDS=0; date +%s%3N; echo $SECONDS
1506318780647
0.0017870000
date
这里运行 GNU 花费了 1787 微秒。
您可以使用$((SECONDS*1000))
来获取毫秒数,因为两个 shell 都支持浮点算术(注意ksh93
遵循区域设置的小数点标记)。
对于作为浮点数的纪元时间,zsh
有$EPOCHREALTIME
:
$ zmodload zsh/datetime
$ echo $EPOCHREALTIME
1506318947.2758708000
并且ksh93
可以使用"$(printf '%(%s.%N)T' now)"
(请注意,ksh93
命令替换不会分叉进程,也不使用内置管道,因此不像其他类似 Bourne 的 shell 那样昂贵)。
您还可以$EPOCHREALTIME
使用以下方式定义变量:
$ EPOCHREALTIME.get() { .sh.value=$(printf "%(%s.%6N)T");
$ echo "$EPOCHREALTIME"
1506333341.962697
对于自动时间戳,您还可以使用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
在 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
根据您的用例,您可能可以依靠 来moreutils
进行ts
时间戳:
$ (date +%s.%6N; date +%s.%6N) | ts %.s
1506319395.000080 1506319394.970619
1506319395.000141 1506319394.971972
(给出通过管道ts
从 的输出读取行的时间)。date
或者对于输出行之间的时间:
$ (date +%s.%6N; date +%s.%6N) | ts -i %.s
0.000011 1506319496.806554
0.000071 1506319496.807907
如果你想获取运行给定命令(管道)所花费的时间,你还可以使用关键字,用intime
调整格式:$TIMEFORMAT
bash
$ TIMEFORMAT=%E; time date
Mon 25 Sep 09:51:41 BST 2017
0.002
这些时间格式指令最初来自 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
(第一个数字(0
此处)表示应该对花费超过那么多秒的命令进行计时,第二个数字指定格式)。
答案3
- 首先该
date
工具不是 bash 的一部分,它是一个外部工具。 - 如果没有外部命令,Bash 本身就无法做到这一点。
- 使用外部命令执行此操作无法通过您的单行标准。
因此,你不能这样做。
但是,如果您允许 2 行,或者为此编写一个函数,则可以这样做,正如注释中所写的那样。
扩大:您可以使用更新的 bash (>4.2) 来完成此操作,即使作为单行命令,尽管它不会是您编写过的最简单的 bash 行。检查另一个答案。
答案4
太长了;博士
$ date +'%s%3N'
1506298414529
巴什
自 bash 4.2 起,可以使用严格的 bash 解决方案(无外部可执行文件):
$ printf '%(%s)T\n' "-1"
1506298414
$ printf '%(%Y%m%d-%H:%M:%S)T\n' "-1"
20170924-20:13:34
但这还不允许毫秒或纳秒。
日期
要获取毫秒或纳秒,您需要使用 GNU 日期,如下所示:
$ printf '%s\n' "$(date +'%Y%m%d-%H:%M:%S.%N')"
20170924-20:13:34.326113922
或者
$ printf '%s\n' "$(date +'%s.%N')"
1506298414.529678016
秒小数部分的 3 位数字限制可以使用%.3f
printf 的格式生成:
$ printf '%.3f\n' "$(date +'%s.%N')"
1506298414.529
或者更好的是,使用减少日期纳秒格式允许的位数的功能:
$ printf '%s\n' "$(date +'%s.%3N')"
1506298414.529
然后,可以删除该点:
$ printf '%s\n' "$(date +'%s%3N')"
1506298414529
当然,在这种情况下,更简单的解决方案(没有 printf,而不是确切要求的)似乎更合适:
$ date +'%s%3N'
1506298414529