Printing current time in milliseconds or nanoseconds with printf builtin

后端 未结 2 1176
独厮守ぢ
独厮守ぢ 2021-01-05 07:05

We can print the current time with the builtin printf function, without needing to invoke an external command like date, like this:



        
相关标签:
2条回答
  • 2021-01-05 07:42

    bash v5 and $EPOCHREALTIME

    • EPOCHREALTIME floating point value with micro-second granularity

    • EPOCHSECONDS the number of seconds since the Unix Epoch

    Correct way

    Simply:

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

    1. 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, if I ask for same variable two time in same line:

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

    or more clearly:

    printf "%s\n" ${EPOCHREALTIME#*.} ${EPOCHREALTIME#*.}
    761893
    761925
    
    echo $((  -10#${EPOCHREALTIME#*.} + 10#${EPOCHREALTIME#*.} ))
    37
    

    Same on my raspberry-pi:

    printf "%s\n" ${EPOCHREALTIME#*.} ${EPOCHREALTIME#*.}
    801459
    801694
    
    echo $((  -10#${EPOCHREALTIME#*.} + 10#${EPOCHREALTIME#*.} ))
    246
    

    So inquiring this two time for building interger part and fractional part is separated process could lead to issues: (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)

    2. 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.

    Correct: This is not a bug! Mixing time() and gettimeofday() is a bug!

    So avoid using both together !!!

    3. 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
    
    0 讨论(0)
  • 2021-01-05 07:45

    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.

    0 讨论(0)
提交回复
热议问题