
/usr/bin/time: not the command you think you know - activatedgeek
https://hackernoon.com/usr-bin-time-not-the-command-you-think-you-know-34ac03e55cc3
======
jimrandomh
The reason 'time' is a builtin is because it can time a complete shell
pipeline, not just a single command. If you type

    
    
        time foo |bar
    

Then the result is the total time taken by foo and bar together. This requires
it to have special-case syntax. Whereas

    
    
        /usr/bin/time foo |bar
    

Would run foo and give its time statistics as input to bar.

~~~
jcoffland
Run these commands for a better demonstration:

    
    
        time echo | sleep 1
    

vs.

    
    
        /usr/bin/time echo | sleep 1
    

The former times the entire pipeline whereas the later only times the first
command in the pipe.

~~~
fnord123
This also gives the time of the entire pipeline (since the commands all begin
together and only finish when the last command has completed):

echo | /usr/bin/time sleep 1

~~~
roblabla
I'm pretty sure this is wrong.

As an example :

sleep 2 | /usr/bin/time sleep 1

This gives an output of "1 second" even though the entire pipeline took 2
seconds.

~~~
fnord123
Ok you can wangle your way out of it. But if you're doing something like `grep
"$something" | /usr/bin/time some_job` then generally you're fine.

~~~
perennate
Wouldn't the system/user time be wrong though?

------
graton
On bash you can do this to have it not use an alias or built-in command:

    
    
      $ \time echo
    
      0.00user 0.00system 0:00.00elapsed 100%CPU (0avgtext+0avgdata 1764maxresident)k
      0inputs+0outputs (0major+66minor)pagefaults 0swaps
    

The built-in version:

    
    
      $ time echo
    
    
      real    0m0.000s
      user    0m0.000s
      sys     0m0.000s

~~~
yrro
Is this behaviour documented anywhere? The closest I can find is
[https://www.gnu.org/software/bash/manual/bashref.html#Aliase...](https://www.gnu.org/software/bash/manual/bashref.html#Aliases)
which remarks,

> The first word of each simple command, if unquoted, is checked to see if it
> has an alias.

I observe that t\ime works just as well as \time.

~~~
sigjuice
_t\ime_ is rather surprising. Is there any documentation or explanation on why
it does what it does?

~~~
yrro
[https://www.gnu.org/software/bash/manual/bashref.html#Escape...](https://www.gnu.org/software/bash/manual/bashref.html#Escape-
Character)

I think it's because using the backslash to escape counts as quoting. So the
following are all equivalent:

    
    
      $ \time foo
      $ t\ime foo
      $ 'time' foo

------
dredmorbius
Since the article doesn't clarify on shell command priority, commands are
search in order:

1\. Alias expansion.

2\. Defined shell functions.

3\. Built-in shell functions.

4\. Command path.

If you provide an unqualified command that matches more than one of these
elements, the _first match_ wins.

Prepending a backstroke: "\command", will inhibit alias expansion. E.g.:

    
    
        alias date="echo no date"
        date
        \date
    

Should return "no date", and your current system date, respectively.

To invoke a system command directly, call the full path. If you don't feel
like running the fish shell (not that there's anything wrong with that).

Some simple shells (e.g., dash, and IIRC the original Bourne shell, though
that is _not_ what you'll find as /bin/sh on most modern systems) don't
include a time builtin, and can invoke the system time command directly.

~~~
knome
Perhaps, but time is a syntactic form, like `if` or `case`.

[https://www.gnu.org/software/bash/manual/html_node/Pipelines...](https://www.gnu.org/software/bash/manual/html_node/Pipelines.html#Pipelines)

~~~
dredmorbius
I'm sorry, that's not clear to me. What's the significance exactly?

~~~
knome
A command takes a series of arguments, stdin/out/err pipes and returns an
error code on completion.

time prefixes a pipeline ( the most basic case being a single command without
a pipe into another ), a command block { ... } a subshell block ( ... ), a for
statement, an if statement, just whatever really.

This "time" would output how long "a" took to run:

    
    
        /usr/bin/time a b c | { d e ; f g ; } | h i ;
    

This "time" outputs how long the pipeline "a", "d", "f", and "h" took to run:

    
    
        time a b c | { d e ; f g ; } | h i ; 
    

This is a syntax error:

    
    
        /usr/bin/time { d e ; f g ; }
    

This returns how long the command group takes to run:

    
    
        time { d e ; f g ; }
    

Further examples:

    
    
        $ time sleep 10 | sleep 1 ;
        
        real	0m10.073s
        user	0m0.000s
        sys	0m0.000s
        $ time sleep 1 | sleep 10 ;
        
        real	0m10.003s
        user	0m0.000s
        sys	0m0.000s
        $ /usr/bin/time sleep 10 | sleep 1 ;
        0.00user 0.00system 0:10.00elapsed 0%CPU (0avgtext+0avgdata 1796maxresident)k
        0inputs+0outputs (0major+80minor)pagefaults 0swaps
        $ 
        $ /usr/bin/time sleep 1 | sleep 10 ;
        0.00user 0.00system 0:01.00elapsed 0%CPU (0avgtext+0avgdata 1808maxresident)k
        0inputs+0outputs (0major+82minor)pagefaults 0swaps
    

Note the two /usr/bin/time's output their timing information as soon as the
first command is done, but the pipeline doesn't return until both commands
have exited.

> hope this rant helps in some way

~~~
dredmorbius
Thanks, that _does_ raise some interesting points.

What of:

    
    
        /usr/bin/time ( sleep 1 | sleep 10 )
    

Explicitly invvoking a subshell. Which I understand the builtin to be doing.

------
metafunctor
A more interesting command is /bin/[.

Yep, it's the same as /bin/test, but basically requires the last argument to
be a ]. Typically /bin/test and bin/[ are hard links, that is, they are
physically the same file and share the same inode(s).

You know, so you can type

    
    
      if [ $foo = $bar ]; then
        yes
      else
        exit
      fi
    

This actually _runs_ the command /bin/[, except most of the time it doesn't,
because it's built-in to the shell.

As as side-note, an article like this should at least mention which OS and
shell the author is using.

~~~
Elrac
> As as side-note, an article like this should at least mention which OS and
> shell the author is using.

Very much agree! I just tried this on my 2.6.32 RHEL system, and it's never
heard of "-l". It outputs very similar-looking information as in the article,
though, when given "-v" .

------
halostatue
In both bash and zsh, you can force the shell to use $PATH for lookup
(bypassing functions and shell builtins) by calling a builtin name with
'command' ('command time -l ls'). You can equivalently force a builtin with
'builtin', but that does not work with reserved words (and 'time' is a shell
reserved word).

~~~
ramshorns
TIL that a shell reserved word is different from a shell builtin.

[http://unix.stackexchange.com/questions/267761/differences-b...](http://unix.stackexchange.com/questions/267761/differences-
between-keyword-reserved-word-and-builtin)

~~~
xelxebar
Yeah. Shell semantics can be pretty unintuitive sometimes. I often find it
helpful to translate these ideas to standard programming language terms.

* Commands are like functions * Commands in /bin etc. are like library functions * Builtins are like a language's primitive functions * Keywords are keywords

------
hobarrera

        $ which time
        time: shell reserved word
        $ ls /usr/bin/time /bin/time
        ls: cannot access '/usr/bin/time': No such file or directory
        ls: cannot access '/bin/time': No such file or directory
    

Looks like something specific to the author's distribution.

~~~
kylek
Exists on a debian(/testing?) system I've:

$ time ls / bin boot dev etc home lib lib64 lost+found media mnt opt proc root
run sbin srv sys tmp usr var

real 0m0.004s

user 0m0.000s

sys 0m0.000s

$ /usr/bin/time ls /

bin boot dev etc home lib lib64 lost+found media mnt opt proc root run sbin
srv sys tmp usr var

0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 2304maxresident)k

0inputs+0outputs (0major+109minor)pagefaults 0swaps

Edit: formatting

~~~
kevinoid
Sometimes. It depends whether you have the time[1] package installed.

1\.
[https://packages.debian.org/sid/time](https://packages.debian.org/sid/time)

------
7171u
I had to use "\--verbose" instead of "-l" in my RHEL7

    
    
      \time --verbose  echo

~~~
aij
I bet you're using GNU time rather than BSD time.

It did seem odd to me that the author didn't bother to mention which OS he is
using, though from the hostname I have a pretty good guess.

------
brendangregg
No, /usr/bin/time is indeed what I know, and its extended stats is why I
suggested using it in my last perf book (time -v).

"/usr/bin/time: not the command you think you know" -> "/usr/bin/time: may not
be the command you think you know"

There, I fixed the title.

~~~
an_account
/r/iamverysmart

------
d4l3k
/usr/bin/time doesn't seem to be a thing on my Arch Linux install. ¯\\_(ツ)_/¯

~~~
fbernier
Same here. I had to fetch it via pacman. Also, the -l option doesn't exist on
the non-bsd version.

~~~
tomsmeding
I love arch. Sudo doesn't exist until you install it (directly or indirectly).

~~~
jcoffland
That's the same on Debian.

~~~
ajsalminen
Yeah, I don't think it's all that rare for sudo to be an optional package not
installed by default in distributions. At least that used to be the case back
when I used try various ones a lot more.

------
devnonymous
So also are /usr/bin/{cd,[,echo,pwd,fg,..etc} a lot of them having subtle
differences with their corresponding shell builtins. Most of the time the
differences are not worth the hassle to remember, unless you somehow end up on
a system with a broken filesystem (for example where /lib or /usr/lib is
destroyed) and need to rescue stuff.

------
cmurf
On Fedora Linux, '/usr/bin/time -v <command>' rather than -l.

------
saagarjha
So it's just like /usr/bin/cd–a builtin that also has a binary.

~~~
tyingq
How would that work? A forked/execed subprocess somehow forcing chdir() in
it's parent?

Guessing it isn't terribly useful.

~~~
zwp
I too don't see how it is useful but it's certainly a thing. The Solaris
implementation looks like this:

    
    
        #!/bin/ksh -p
        # ...
        cmd=`basename $0`
        $cmd "$@"
    

I just noticed that the what(1)-string (I haven't seen on of those for a long
time) references "alias.sh", perhaps this is a clue?

    
    
        #ident  "@(#)alias.sh   1.2     00/02/15 SMI"
    

Were builtins actually aliases in an early shell? I still don't understand how
this works though.

~~~
tyingq
That's a funny implementation. It would end up being an infinite loop for any
command that wasn't a builtin, or didn't have an identically named thing
higher up the PATH. Like if you renamed it from /usr/bin/cd to /usr/bin/mycd.

