A Tale Of Two Times
< ngelover> time -l says unknown option to me, shouldn't it print
the usage()
< thrig> which time are you running?
< ngelover> time(1)
< thrig> really?
< ngelover> well yes
The claim is that "time" disagrees with the documentation for time(1) on OpenBSD, and the evidence for this may look pretty solid:
$ man 1 time | fgrep -- -l
time [-lp] utility [argument ...]
-l The contents of the rusage structure are printed.
The flag [-l] is an extension to that specification.
$ time -l sleep 1
ksh: time: -l unknown option
Documentation problems are not unknown, for example cal(1) has a slightly complicated interface in that a single argument is taken as a year, and that slot instead becomes the month if a second number is provided...but the error message only mentions the month, not the maybe instead a year thing:
$ cal Germinal
cal: invalid month: use 1-12 or a name
However, in this case both the time(1) manual and "time" are correct. The problem here, as alluded to by my question--"which time are you running?"--is that there are multiple implementations of "time". The ksh shell has a reserved word "time" that will be run in favor of anything found in PATH:
time [-p] [pipeline]
The time reserved word is described in the Command execution
section.
Note the lack of an -l flag in this documentation.
Other bournelikes--shells vaguely based on /bin/sh--are similar; ZSH also has an internal "time" or can run whatever turns up in PATH:
$ exec zsh
% whence -a time
time
/usr/bin/time
% exec ksh
There are various ways to bypass the internal "time" command, if you really do want to use time(1) and not the one provided by the shell,
/usr/bin/time -l sleep 1
command time -l sleep 1
\time -l sleep 1
exec time -l sleep 1
though that last one might not be useful if the terminal output vanishes, and the \time trick to bypass aliases is also a bit clever and probably should not be used.
$ alias echo='builtin echo ohce'
$ echo foo
ohce foo
$ \echo foo
foo
$ unalias echo
Note that "echo" also has shell-internal and external command implementations, and like "time" the different implementations vary. Hence the recommendation to use printf(1) in shell scripts that make at least some claim to portability.
$ which bash
which: bash: Command not found.
Anyways, how can you know what "time" command you are actually running, besides from studying the system for probably too long? One way is process tracing; our hypothesis is that we are running time(1), so ideally we would like to see evidence for that. Care must be taken to setup a correct test: are you running the right commands, and tracing the right processes?
$ ktrace -i ksh -c 'time sleep 1'
0m01.00s real 0m00.00s user 0m00.00s system
$ kdump | grep fork
34187 ksh CALL fork()
34187 ksh RET fork 70455/0x11337
70455 ksh RET fork 0
The fork here is ksh launching sleep(1); there is no time(1) being run. If time(1) were being run, it should appear as " 1234 time" for some random process ID, which it does if we use the fully qualified path to time:
$ kdump | perl -nle 'print if m/^\s*\d+\s+time/a'
$ ktrace -i ksh -c '/usr/bin/time sleep 1'
1.00 real 0.00 user 0.00 sys
$ kdump | perl -nle 'print if m/^\s*\d+\s+time/a' | sed 1q
67399 time NAMI "/usr/libexec/ld.so"
$ kdump | grep fork
74945 ksh CALL fork()
74945 ksh RET fork 67399/0x10747
67399 ksh RET fork 0
67399 time CALL vfork()
67789 time RET vfork 0
67399 time RET vfork 67789/0x108cd
A major problem here is that we must have a doubt that time(1) is actually being run; knowledge of shell reserved words and builtins would help increase that doubt. Lacking doubt, it is easier to claim a documentation problem or some other bug on the assumption that time(1) is being run.
tags #debug #sh #unix #zsh