问题
We can print the current time with the builtin printf
function, without needing to invoke an external command like date
, like this:
printf '%(%Y-%m-%d:%H:%M:%S)T %s\n' -1
# sample output: 2019-03-30:17:39:36,846
How can we make printf to print milliseconds or nanoseconds as well? Using %3N
or %N
in the format string doesn't work:
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
However, the date command works fine:
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
This is on a Red Hat Linux version 7.3.
回答1:
In bash
5, you can get microsecond precision from EPOCHREALTIME
. However, printf
itself has no way to access that directly, so you need to extract the microseconds yourself.
$ echo $EPOCHREALTIME; printf '%(%F:%T)T.%d\n' "$EPOCHSECONDS" "${EPOCHREALTIME#*.}"; echo $EPOCHREALTIME
1554006709.936990
2019-03-31:00:31:49.937048
1554006709.937083
This takes a little time, but the result appears to be accurate to about 0.05 milliseconds.
回答2:
bash v5 and $EPOCHREALTIME
Correct way
Simply:
IFS=. read ESEC NSEC <<<$EPOCHREALTIME
printf '%(%F:%T)T.%06.0f\n' $ESEC $NSEC
About $EPOCHREALTIME
Please care:
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.
So:
echo $EPOCHREALTIME... $EPOCHREALTIME
1572000683.886830... 1572000683.886840
or more clearly:
printf "%s\n" ${EPOCHREALTIME#*.} ${EPOCHREALTIME#*.}
761893
761925
So inquiring this two time for building interger part and fractional part is separated process could lead to an issue: (On same line first access to $ EPOCHREALTIME
could give: NNN1.999995
, then next: NNN2.000002
. Than result will become: NNN1.000002
with 1000000 micro-second error)
WARNING! About mixing $EPOCHSECONDS
and $EPOCHREALTIME
Using both together not only lead to first mentioned bug!
$EPOCHSECONDS
use call to time()
which is not updated constantly, while $EPOCHREALTIME
use call to gettimeofday()
! So results could differ a lot:
I found This answer to time() and gettimeofday() return different seconds with good explanation.
If I try on my host:
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
}
(
Nota: I use read -t
instead of sleep
, because sleep
is not builtin
Nota2: You could play with argument of function to change value of read timeout (sleep)
)
This could render something lile:
$ 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
Where integer part of $EPOCHREALTIME
could increase more than 8000 microseconds before $EPOCHSECONDS
(on my host).
Nota: This seem to be linked to some bug, result could differ a lot between different hosts or on same host after reboot, and other things... Strangely I could reproduce them on a lot of different hosts (Intel Core, Intel Xeon, Amd64..) but not on raspberry pi!? (Same Debian bash v5.0.3(1)-release), different kernel version.
So avoid using both together!
About printf "..%06.0f"
Nota: I use %06.0f
instead of %d
to ensure $NSEC
to be interpreted as a decimal (float), (prevent octal interpretation if variable begin by 0
).
Compare:
printf "nn.%06.0f\n" 012345
nn.012345
printf "nn.%06.0f\n" 098765
nn.098765
and
printf "nn.%d\n" 012345
nn.5349
printf "nn.%d\n" 098765
-bash: printf: 098765: invalid octal number
nn.0
Sample run
Little test:
wait until next second, then print current time with micro-seconds
while ! read -t .$((1000000-10#${EPOCHREALTIME#*.})) foo; do
IFS=. read ESEC NSEC <<< $EPOCHREALTIME;
printf '%(%F:%T)T.%06.0f\n' $ESEC $NSEC;
done
You could end this test by pressing 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
来源:https://stackoverflow.com/questions/55436913/printing-current-time-in-milliseconds-or-nanoseconds-with-printf-builtin