Bash – What can the external command `time` and the reserved word `time` time

bashtime-utility

Bash Reference Manual says

The use of time as a reserved word permits the timing
of shell builtins, shell functions, and pipelines. An external time command cannot time
these easily.

  1. What can an external command time time?

    Only a simple external command? Nothing else?

    Which rule prevents it from timing other things? Does the rule
    belong to the shell (bash) or the implementation of the external
    command time?

    Btw, I am running Ubuntu, so time is from Debian.

  2. Can the Bash's reserved word time time all the things that can
    run? If not, what can't it time?

  3. What are the things that the external time can time but the reserved word time can't?

Best Answer

My answer is about Linux.

  1. Which rule prevents it from timing other things?

I guess it gets time info only for processes since it actually uses wait4 system call for getting this time info. The first parameter of wait4 is the pid of a process. So /usr/bin/time always call execve and then wait4.

By the way:

execve() executes the program pointed to by filename. filename must be either a binary executable, or a script starting with a line of the form: #! interpreter [optional-arg]

So /usr/bin/time needs something that can be executed by execve

  1. Can the Bash's reserved word time time all the things that can run?

I guess, yes. It is implemented as two group of calls:

10:06:03 getrusage(RUSAGE_SELF, {ru_utime={0, 152976}, ru_stime={0, 465929}, ru_maxrss=4052, ru_ixrss=0, ru_idrss=0, ru_isrss=0, ru_minflt=6214, ru_majflt=1, ru_nswap=0, ru_inblock=368, ru_oublock=0, ru_msgsnd=0, ru_msgrcv=0, ru_nsignals=0, ru_nvcsw=6349, ru_nivcsw=12}) = 0
10:06:03 getrusage(RUSAGE_CHILDREN, {ru_utime={2, 259656}, ru_stime={1, 888712}, ru_maxrss=21176, ru_ixrss=0, ru_idrss=0, ru_isrss=0, ru_minflt=64297, ru_majflt=4, ru_nswap=0, ru_inblock=272, ru_oublock=368, ru_msgsnd=0, ru_msgrcv=0, ru_nsignals=0, ru_nvcsw=56077, ru_nivcsw=386}) = 0

run a command or builtin

10:06:04 getrusage(RUSAGE_SELF, {ru_utime={0, 152976}, ru_stime={0, 476927}, ru_maxrss=4052, ru_ixrss=0, ru_idrss=0, ru_isrss=0, ru_minflt=6243, ru_majflt=1, ru_nswap=0, ru_inblock=368, ru_oublock=0, ru_msgsnd=0, ru_msgrcv=0, ru_nsignals=0, ru_nvcsw=6399, ru_nivcsw=13}) = 0
10:06:04 getrusage(RUSAGE_CHILDREN, {ru_utime={2, 271654}, ru_stime={1, 903710}, ru_maxrss=21176, ru_ixrss=0, ru_idrss=0, ru_isrss=0, ru_minflt=64514, ru_majflt=4, ru_nswap=0, ru_inblock=272, ru_oublock=368, ru_msgsnd=0, ru_msgrcv=0, ru_nsignals=0, ru_nvcsw=56079, ru_nivcsw=406}) = 0

So I guess bash just calculate a difference and prints it. In that way it can measure anythins like own builtins or child process

  1. What are the things that the external time can time but the reserved word time can't?

Actually both system calls getrusage and wait4 get from a kernel struct rusage usage. However bash time prints limited number of fields from this structure. This is from man 1 time:

Note: some shells (e.g., bash(1)) have a built-in time command that provides less functionality than the command described here.

$/usr/bin/time -v  seq 10000
    Command being timed: "seq 10000"
    User time (seconds): 0.01
    System time (seconds): 0.01
    Percent of CPU this job got: 24%
    Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.12
    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): 2640
    Average resident set size (kbytes): 0
    Major (requiring I/O) page faults: 0
    Minor (reclaiming a frame) page faults: 199
    Voluntary context switches: 8
    Involuntary context switches: 24
    Swaps: 0
    File system inputs: 0
    File system outputs: 8
    Socket messages sent: 0
    Socket messages received: 0
    Signals delivered: 0
    Page size (bytes): 4096
    Exit status: 0