1. Overview
Logging time is critical for monitoring the status of our computer. Log files are timestamped for this exact reason.
Tracking time can be useful for seeing the start and the duration of a command. We can also use timestamps as filters, allowing us to analyze specific events.
In this tutorial, we’ll look at some ways to prefix a timestamp to the output of a Linux command. The commands presented here are written with the Bash shell in mind, so they might not work with other shells.
2. Using ts
The ts command prefixes a timestamp to each line of output. It’s part of the moreutils package.
When we run ts in the shell, it reads from stdin and prepends the time once we hit Enter:
$ ts
welcome to
Feb 17 14:46:49 welcome to
baeldung
Feb 17 14:46:51 baeldung
We can change the default date format with strftime formatting:
$ ts "%F %T"
welcome to baeldung
2023-02-17 14:53:12 welcome to baeldung
The %F corresponds to the year-month-day format and the %T corresponds to the hour:minute:second.
However, ts is more powerful when piped, as we can redirect the output directly:
$ echo -e "welcome to\nbaeldung" | ts
Feb 17 15:01:49 welcome to
Feb 17 15:01:49 baeldung
As we can see above, on every newline of the output, ts prepends a timestamp.
The -e flag in the echo command enables the interpretation of backslash escapes. This way, it’s possible to use newline characters.
Additionally, let’s use the -s flag to show incremental timestamps:
$ (echo "welcome to"; sleep 3; echo "baeldung") | ts -s
00:00:00 welcome to
00:00:03 baeldung
While the -s flag shows the elapsed time since the start of the execution, the -i flag shows the elapsed time since the last timestamp.
3. Using awk
awk is a command for interpreting scripts written in the AWK programming language. We can use it for processing and manipulating text.
Now, let’s look at how we can use it to timestamp a command:
$ echo "welcome to baeldung" | awk '{ print strftime("%Y-%m-%d %H:%M:%S"), $0; fflush(); }'
2023-02-20 15:40:08 welcome to baeldung
In this example, we’re piping the output of the echo command to awk. The print statement uses the piped output, with the $0.
Similarly to ts, it also uses the strftime format. This means we can change the timestamp format by changing the input of the function:
$ echo "welcome to baeldung" | awk '{ print strftime("%a, %b %d %Y %T"), $0; fflush(); }'
Mon, Feb 20 2023 16:09:14 welcome to baeldung
Lastly, the fflush function flushes the output buffer. This way, the output of commands won’t be delayed in the buffer and will be immediately printed to the console or file.
4. Using perl
The perl command (Practical Extraction and Report Language) interprets Perl language programs. This language is known for its string manipulation features.
Let’s pipe the output of echo to a perl program:
$ echo "welcome to baeldung" | perl -pe 'print localtime . " ";'
Tue Feb 21 13:41:46 2023 welcome to baeldung
The -p flag loops over the program for each line in the input, as well as prints the input string of each loop at the end of the output.
The -e flag enables perl to execute the code that follows in the command instead of executing a file.
As shown above, we use the localtime function to retrieve the timestamp. This is the default format when we print, but the localtime function returns more information. We also use a . to append in perl, adding a space at the end of the timestamp.
Let’s see how we can change the date format:
$ cat timestamp.pl
#!/usr/local/bin/perl
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $dsav) = localtime();
$mon = $mon + 1;
$year = $year + 1900;
print "$mday/$mon/$year $hour:$min ";
$ echo "welcome to baeldung" | perl -p timestamp.pl
21/2/2023 15:47 welcome to baeldung
In this case, we’re using the output of localtime to change our date format.
However, we need to make some adjustments. The $mon value is the month number, starting from 0, hence why we have to add 1. In the case of the $year, it’s the number of years since 1900, which is the reason why we have to add it to get the current year.
5. Using date
date is a Linux command line utility. We use it to set the system date or to output the current date.
5.1. With echo
Let’s see how we can join the echo command with date to prepend time to a string:
$ echo $(date; echo "welcome to baeldung";)
Wed Feb 22 10:51:55 AM WET 2023 welcome to baeldung
Here, we’re using $() to receive the output of a command. The text between the parenthesis is executed as a command, and its output is returned.
Although the timestamp format is verbose, we can modify it:
$ echo $(date +"%F %T"; echo "welcome to baeldung";)
2023-02-22 11:04:15 welcome to baeldung
However, using date in this way only works for one-lined inputs:
$ echo $(date +"%F %T"; echo -e "welcome\nto baeldung";)
2023-02-22 11:09:21 welcome to baeldung
The command concatenates every line and prefixes a single date to the whole input.
Let’s circumvent this by using a while loop:
$ echo -e "welcome\nto baeldung" | while read line; do echo $(date) $line; done
Wed Feb 22 11:19:10 AM WET 2023 welcome
Wed Feb 22 11:19:10 AM WET 2023 to baeldung
Nevertheless, this isn’t an ideal solution. Iterating over text using shell loops isn’t recommended, as it can consume more time and resources.
5.2. With sed
Another alternative we can use with date is the sed command. It’s a stream editor known for its string transformation features.
Let’s look at how we can use sed to prepend the date command:
$ echo "welcome to baeldung" | sed "s/^/$(date) /"
Thu Feb 23 02:07:13 PM WET 2023 welcome to baeldung
The s stands for substitution. The following slashes showcase two patterns: the first one is matched and replaced with the second one.
As shown above, we’re adding the output of date to the beginning of the string (^).
Let’s now use sed with a multi-line input:
$ (echo "welcome to"; sleep 2; echo "baeldung") | sed "s/^/$(date) /"
Thu Feb 23 02:17:06 PM WET 2023 welcome to
Thu Feb 23 02:17:06 PM WET 2023 baeldung
If we analyze the output, we can see there’s something wrong. Although we used the sleep command, both timestamps are the same.
This happens because sed only processes the date command once, at the beginning of execution. Therefore, the same date is prepended to the whole input.
We can tweak our command to avoid this:
$ (echo 'welcome to'; sleep 2; echo 'baeldung';) | sed "x;s/.*/date /e;G;s/\n/ /"
Thu Feb 23 02:24:43 PM WET 2023 welcome to
Thu Feb 23 02:24:45 PM WET 2023 baeldung
First, we use the x flag to empty the pattern space and save it in the hold space. This way, we start out with an empty string.
Then, we substitute .* (any character occurring zero or more times) with the string “date “. However, since we’re using the e flag, this string is evaluated as a shell command. Instead of “date “, we have the output of the command, which is the timestamp.
Since we used the x flag, the contents of the hold space are the original output of the command. The G flag appends these contents to the output string.
Lastly, we do another substitution to remove the extra newline that the date command added.
6. Conclusion
In this article, we looked at different ways to prepend a timestamp to the output of a command. We used different text processing utilities, as well as different ways of timestamping.
All of the processes described use Bash pipes. Because of this, these approaches work for the output of commands and text files alike since the tools add the timestamps on every line break.
Finally, we also managed to change the format of the date. We can present different units of time, as well as present time as an absolute value or relative to other timestamps.