Unix bash shell and the time command output

I am preparing some code for a another blog post I came across some very unexpected behavior on my terminal that really got me thinking 🤔. Time was behaving in 2 different ways depending on what it seems an entirely unrelated reason.

Photo by Pixabay on Pexels.com

Check this output out:

$ time go run github.com/efrag/blog-posts/go_routines/ one-routine-wait
real    0m1.556s
user    0m1.545s
sys     0m0.064s

Looks pretty normal right? I run the command prefixed with time and I got the expected output. But check what happens when I run it like this:

$ GOMAXPROCS=10 time go run github.com/efrag/blog-posts/go_routines/ one-routine-wait
1.54user 0.07system 0:01.58elapsed 102%CPU (0avgtext+0avgdata 46812maxresident)k
0inputs+3112outputs (0major+16202minor)pagefaults 0swaps

That looks pretty weird right? Did my terminal suddenly decide that I am an “advanced” user and went all out with a more precise/detailed view of the output for time? Surely that can’t be the case 🤪 – so what’s going on here?

I decided to strip out all the things and just go with a very plain command to eliminate any doubt. Let’s just use a simple ls on a directory and run things again:

$ time ls -l
real    0m0.005s
user    0m0.005s
sys     0m0.000s

and one more time with:

$ a=1 time ls -l
0.00user 0.00system 0:00.00elapsed 75%CPU (0avgtext+0avgdata 3032maxresident)k
0inputs+0outputs (0major+162minor)pagefaults 0swaps

Again same output but now it got me thinking – could this be something related with my terminal instead? Well, kinda. After a lot of looking around and reading the man pages for both bash and time it turns out that there is a logical explanation after all ¯\_(ツ)_/¯.

Clue #1

Bash offers extended functionality to the terminal. When in doubt if something is actually part of bash or a standard unix command you can do the following:

$ type which
which is /usr/bin/which

$ type time
time is a shell keyword

Turns out when running time on the terminal I was not using the Unix time command rather the time keyword from bash.

Clue #2

Looking further in the bash documentation there is a section that talks about pipelines in bash and the example there demonstrates the use of the keyword time.

A pipeline is a sequence of one or more commands separated by one of the control operators | or |&. The format for a pipeline is:

[time [-p]] [ ! ] command [ [|⎪|&] command2 … ]

If the time reserved word precedes a pipeline, the elapsed as well as user and system time consumed by its execution are reported when the pipeline terminates.

Clue #3

The man page for the unix time command actually verifies this as well as it says:

Users of the bash shell need to use an explicit path in order to run the external time command and not the shell builtin variant.

OK – so now that we have all of clues what is actually happening with my command? Turns out that the explanation is quite simple. Bash will use the builtin variant when we define a pipeline that starts with the keyword time which follows the POSIX standard of reporting the time statistics.

But, when we put anything before the keyword time then bash will not default to its builtin keyword but rather use the external unix command whose output format is determined as

%Uuser %Ssystem %Eelapsed %PCPU (%Xtext+%Ddata %Mmax)k
%Iinputs+%Ooutputs (%Fmajor+%Rminor)pagefaults %Wswaps

which is what I was seeing when defining an env variable before running my command 🤯.

Leave a Reply