While working on the Linux command line I often like to know how long it took a command to complete. It helps to check efficiency of different processes, check on time-sensitive, long-running commands, or just to satisfy your curiosity. In this tutorial, we will look at several ways to print the execution time of a command in Linux.

As with anything in Linux, there are several different ways to accomplish this. The most basic method is to add time in front of a command, like so:

$ time cat /dev/random | rngtest -c 10 2> /dev/null
real 0m4.587s
user 0m0.003s
sys 0m0.021s

NOTE: If you run the above, chances are you are running the bash shell keyword, not the binary time command. More on that below.

Before we continue to discuss printing the time a command took to execute, I feel it's very important that we discuss the fact that there are two different "time" commands. There is a shell keyword and a binary version.

Let's take a deeper look at the time shell keyword vs the time binary.

Time Shell Keyword vs Time Binary

The shell keyword (or reserved word) is built in to the shell itself. It does not have options such as formatting or output control. However, since it is part of the shell, it allows the timing of other shell builtins, functions and pipelines.

Let's do a little experiment. With the bash keyword, we can time how long this pipeline took to complete:

$ time cat /usr/share/dict/words | dd of=/tmp/test 
9674+1 records in
9674+1 records out
4953598 bytes (5.0 MB, 4.7 MiB) copied, 0.0129688 s, 382 MB/s
real 0m0.015s
user 0m0.003s
sys 0m0.016s

There are two significant things to look at here. The dd command output is printed ABOVE the time output and the time it took to complete (.015s) is at the end of the output. Now let's run the same command through the binary time command:

$ /usr/bin/time cat /usr/share/dict/words | dd of=/tmp/test 
0.00user 0.00system 0:00.01elapsed 27%CPU (0avgtext+0avgdata 1896maxresident)k
0inputs+0outputs (0major+109minor)pagefaults 0swaps
9674+1 records in
9674+1 records out
4953598 bytes (5.0 MB, 4.7 MiB) copied, 0.0177679 s, 279 MB/s

Here, we see the output of dd BELOW the time output. This shows us that the pipe is not being timed by the binary command. We can use verbose output to prove this theory:

$ /usr/bin/time -v cat /usr/share/dict/words | dd of=/tmp/test 
Command being timed: "cat /usr/share/dict/words"
User time (seconds): 0.00
System time (seconds): 0.00
Percent of CPU this job got: 27%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.01
...OUTPUT TRUNCATED...

The second line in the above output shows us the command that is being timed. As suspected, it is only the first command in the pipeline.

So should I always use the shell keyword? ...Not so fast. The time binary command has it's benefits. It has some good features like formatting and sending output to a file but it is important to be aware of it's limitations.

Which Time Am I Using?

Most modern systems have the keyword in their shell. You can find out if you have the time keyword by using the type builtin:

Output if using bash:

$ type time
time is a shell keyword

Output if using z shell:

% type time
time is a reserved word

If you have the keyword in your shell, then when you type "time", you are calling the keyword, not the binary.

You can find the location of the binary using the which command.

$ which time
/usr/bin/time

Using the Binary instead of the Shell Keyword

If your system has the shell keyword, you can use the full path to the binary to call it instead of the keyword.

/usr/bin/time <command>

Or you can escape the keyword and force the system to search your PATH for the executable binary.

\time <command>

If you prefer to use the time program (binary) instead of the shell keyword by default, you can make an alias to supersede the keyword.

alias time="/usr/bin/time"

Output of Time Keyword vs Binary

The builtin shell keyword time outputs three lines showing different pieces of information. Real time depicts time as humans use it, like a regular clock. User shows the user CPU time. Sys shows the system CPU time.

$ time cat /dev/random | rngtest -c 10 2> /dev/null
real 0m4.587s
user 0m0.003s
sys 0m0.021s

The binary time outputs the same information but swaps the term "real" for "elapsed", unless using the -p (portability) option. It also includes additional information like input/output and pagefault counts.

$ /usr/bin/time cat /dev/random | rngtest -c 10 2> /dev/null
Command terminated by signal 13
0.00user 0.01system 0:05.59elapsed 0%CPU (0avgtext+0avgdata 1796maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps

Using the Time Shell Keyword

There isn't much to using the shell keyword: simply call it before a command or string of commands.

$ time find / -name urls 2>/dev/null
/home/savona/Desktop/TEMP/Putorius/AMP/urls

real 0m6.376s
user 0m0.513s
sys 0m1.023s

The only option that exists for the keyword is -p which is explained in the man page as "When in the POSIX locale, use the precise traditional format".

$ time -p find / -name urls 2>/dev/null
/home/savona/Desktop/TEMP/Putorius/AMP/urls

real 1.21
user 0.40
sys 0.54

Using the Time Command Binary

When using the time program you have more options to manipulate the output. Here, we will demonstrate some of these options.

REMEMBER: To use the binary version of time, you either have to call it using the full path or escape the keyword as explained in the "Using the Binary Instead of the Shell Keyword" section above.

One interesting tidbit is that the time command sends it's output to standard error (stderr) by default. Keep this in mind if you plan on doing any redirection.

POSIX Format

The -p (--portability) option does the same with the binary as it does with the keyword. It simply outputs the time in POSIX format:

$ \time -p wget -q https://putorius.net
real 0.44
user 0.05
sys 0.0

Quiet Mode

Using -q (--quiet) will stop time from printing any non-zero exit status. For example, the command used below exits and tells us that the command was terminated by signal 13 (SIGPIPE).

$ \time cat /dev/random | rngtest -c 10 2> /dev/null
Command terminated by signal 13
0.00user 0.01system 0:04.48elapsed 0%CPU (0avgtext+0avgdata 1720maxresident)k
0inputs+0outputs (0major+73minor)pagefaults 0swaps

Issuing -q option would suppress that output.

$ \time -q cat /dev/random | rngtest -c 10 2> /dev/null
0.00user 0.01system 0:05.54elapsed 0%CPU (0avgtext+0avgdata 1768maxresident)k
0inputs+0outputs (0major+78minor)pagefaults 0swaps

Output to a File

Since time prints to stderr, you could redirect stderr to a file. This would also redirect any errors generated by the command being timed. It is much safer to use the -o (--output) option to write the output of time to a file.

$ \time -o output.txt -p wget -q https://putorius.net
$ cat output.txt
real 0.54
user 0.06
sys 0.0

Using this option ONLY saves the time output to the file, not the output of the command being timed.

NOTE: Using -o will overwrite a file if it exists.

Append Output to a File

Using the -a (--append) option to append data to a file that already exists.

It's important to note that unlike other commands, you do not replace -o with -a to append. You have to use -a in addition to -o <file>.

$ \time -o output.txt -a -p wget -q https://putorius.net
$ cat output.txt
real 0.54
user 0.06
sys 0.00
real 0.52
user 0.05
sys 0.00

Verbose Output - Descriptions

This is a very handy feature and I wish more commands would do something similar. The time command does not just spew a bunch of additional information when using verbose mode; it also gives a description of each item's meaning.

$ \time -v wget -q https://putorius.net
Command being timed: "wget -q https://putorius.net"
User time (seconds): 0.05
System time (seconds): 0.00
Percent of CPU this job got: 16%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.35
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 13728
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 5
Minor (reclaiming a frame) page faults: 2007
Voluntary context switches: 21
Involuntary context switches: 1
Swaps: 0
File system inputs: 1344
File system outputs: 88
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0

Formatting Output of Time Command

There are many options for formatting time, memory, IO, and Command information from the time command. Here we will cover a few and give examples to get you started.

Display real time it took for a command to complete

$ \time -f %E wget -q https://putorius.net
0:00.31

You can incorporate text in the formatting as well:

$ \time -f "Real Time: %E" wget -q https://putorius.net
Real Time: 0:00.30

Show percentage of CPU that command used

$ \time -f %P stress -q --cpu 2 --timeout 10
199%

This should give you an idea of the necessary syntax. For a full list of formatting options, see the man page linked below in the resources.

Conclusion

I really enjoyed writing this article and figuring out ways to experiment with the keyword vs binary. We covered basic ways to time a shell command, and discussed the differences between using the shell keyword versus the binary command.

If you found this interesting, please consider following us on Twitter and Facebook. Also, feel free to sign up for the Putorius Newletter.

Resources

Helpful Links