I have a simple bash script:


enp="$(ip route | grep "" | cut -d' ' -f 3)"

while true; do
    vnstat -l -i "$enp" --style 4 --json | awk '{ print strftime("[%d/%b/%Y:%H:%M:%S]"), $0 }' >> raw/caching_server_network.json
    printf -- '-%.0s' {1..100}; echo "" >> raw/caching_server_network.json
    sleep 10

Currently, this script appends a strftime to the start of each vnstat output, and the strftime is updated on every call. Output:

[04/Jan/2022:13:31:17] {"index":1012,"seconds":2024,"rx":{"ratestring":"13.59 Mbit/s","bytespersecond":1698943,"packetspersecond":15220,"bytes":3397887,"packets":30441,"totalbytes":29895276777,"totalpackets":43496469},"tx":{"ratestring":"2.81 Gbit/s","bytespersecond":350918926,"packetspersecond":233842,"bytes":701837853,"packets":467685,"totalbytes":518923724379,"totalpackets":354639132}}
[04/Jan/2022:13:31:41] {"index":1013,"seconds":2026,"rx":{"ratestring":"206.68 Mbit/s","bytespersecond":25834765,"packetspersecond":31840,"bytes":51669531,"packets":63681,"totalbytes":29946946308,"totalpackets":43560150},"tx":{"ratestring":"3.15 Gbit/s","bytespersecond":394123782,"packetspersecond":269030,"bytes":788247565,"packets":538060,"totalbytes":519711971944,"totalpackets":355177192}}

What I would like to do is have the datetime string have milliseconds included in it. But I am facing an issue where the date output is not updating anymore. Every call is just adding the same time to the string.

I have tried the following:


enp="$(ip route | grep "" | cut -d' ' -f 3)"

while true; do
    vnstat -l -i "$enp" --style 4 --json | awk '{ print "'"$(date +%F:%T.%3N)"'", $0 }' >> raw/caching_server_network.json
    printf -- '-%.0s' {1..100}; echo "" >> raw/caching_server_network.json
    sleep 10


04/Jan/2022:13:30:11.743 {"index":975,"seconds":1950,"rx":{"ratestring":"73.04 Mbit/s","bytespersecond":9130121,"packetspersecond":25590,"bytes":18260242,"packets":51181,"totalbytes":29368713172,"totalpackets":42315654},"tx":{"ratestring":"3.90 Gbit/s","bytespersecond":486961964,"packetspersecond":326200,"bytes":973923928,"packets":652400,"totalbytes":498158299423,"totalpackets":340676257}}
04/Jan/2022:13:30:11.743 {"index":976,"seconds":1952,"rx":{"ratestring":"12.23 Mbit/s","bytespersecond":1529119,"packetspersecond":17962,"bytes":3058239,"packets":35924,"totalbytes":29371771411,"totalpackets":42351578},"tx":{"ratestring":"4.18 Gbit/s","bytespersecond":522336815,"packetspersecond":346742,"bytes":1044673631,"packets":693484,"totalbytes":499202973054,"totalpackets":341369741}}

Notice how the datetime in the above output is the same for both lines. How can I get the date to update with each vnstat call while using the date call?


2 Answers 2


The time is changed for each invocation of vnstat, but if vnstat outputs multiple lines, all of them receive the same time. That's because the shell runs the date and inserts its output to the argument to awk.

To get a new timestamp for each line, you need to call the date from within awk. As I don't have vnstat on my machine, I created a simpler example:

#! /bin/bash

while true; do
    for i in {1..3} ; do
        echo $i
        sleep .1
    done  | awk '{ system("date +%F:%T.%3N\\  | tr -d \\\\n"); print $0 }'
    printf -- '-%.0s' {1..80}; echo ""
    sleep 1

The tr removes the newline after the timestamp.

  • Perfect, thanks for the quick help. One last question, the system() part, does that save the date string into the $0? I am just not familiar with that call.
    – Ryan Burch
    Commented Jan 5, 2022 at 16:36
  • 1
    No, it just outputs it.
    – choroba
    Commented Jan 5, 2022 at 16:45
  • or just in the shell, since we're calling the external date utility anyway: ... | while IFS= read -r line; do printf "[%s] %s\n" "$(date +%F:%T.%3N)" "$line"; done
    – ilkkachu
    Commented Jan 5, 2022 at 16:53

Rather than using a shell loop and invoke one awk, date, sleep utility in each iteration (or worse, each line of output of each invocation of your command), I'd do the whole thing in something that can do all of that internally like perl:

perl -MPOSIX -MTime::HiRes=gettimeofday -e '
  while (1) {
    open CMD, "-|", @ARGV;
    while (<CMD>) {
      my ($s,$us) = gettimeofday;
      printf "%s.%03d %s", strftime("%F:%T", localtime$s), $us/1000, $_
    print "-" x 100 . "\n";
    sleep 10;
  }' vnstat -l -i "$enp" --style 4 --json >> file.log

Or in zsh:

zmodload zsh/zselect

while true; do
  vnstat -l -i "$enp" --style 4 --json |
    while IFS= read -r line; do
      print -r ${(%)ts_format} $line
  echo $sep
  zselect -t 1000 # cs
done >> file.log

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .