💾 Archived View for thrig.me › blog › 2023 › 02 › 04 › a-tale-of-two-times.gmi captured on 2023-12-28 at 16:10:42. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2023-04-19)

🚧 View Differences

-=-=-=-=-=-=-

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