Bash – Differences between reserved word and command `time` on shell builtin, function, pipeline and command list

bashtime-utility

In the Bash Reference Manual,

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. Could you explain why the quote says that?

    Is this because of the difference between a reserved word and a
    command, not just limited to the case of time? For example, how does the bash shell
    parse or interpret them differently?

    Or is this only limited to the case of time?

  2. In the following examples,

    why does the external time work on a shell builtin, and a
    pipeline, while the quote say it "cannot time these easily"?

    External time on a shell builtin:

    $ /usr/bin/time echo hello
    hello
    0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 1676maxresident)k
    0inputs+0outputs (0major+78minor)pagefaults 0swaps
    

    External time on a pipeline:

    $ /usr/bin/time sleep 10 | sleep 5
    0.00user 0.00system 0:10.00elapsed 0%CPU (0avgtext+0avgdata 1776maxresident)k
    0inputs+0outputs (0major+79minor)pagefaults 0swaps
    
  3. In the following example, why does the external time on a shell
    function
    fail? What does its error output mean?

    $ function mytest () { sleep 10; }
    
    $ /usr/bin/time mytest
    /usr/bin/time: cannot run mytest: No such file or directory
    Command exited with non-zero status 127
    0.00user 0.00system 0:00.03elapsed 0%CPU (0avgtext+0avgdata 1252maxresident)k
    32inputs+0outputs (0major+30minor)pagefaults 0swaps
    
  4. It seems that the quote does not just apply to timing shell builtins,
    shell functions, and pipelines, but also to timing a group of
    commands
    :

    $ time { echo hello; sleep 3; echo tim; }
    hello
    tim
    
    real    0m3.002s
    user    0m0.000s
    sys 0m0.000s
    
    
    $ /usr/bin/time { echo hello; sleep 3; echo tim; }
    bash: syntax error near unexpected token `}'
    

    Why does the shell say "bash: syntax error near unexpected token
    }" in the case for command /usr/bin/time?

Best Answer

In bash, time is a reserved word, so the shell can parse it the own way and apply rules for it.

Here is the code show how bash parse line start with time reserved word:

static int
time_command_acceptable ()
{
#if defined (COMMAND_TIMING)
  int i;

  if (posixly_correct && shell_compatibility_level > 41)
    {
      /* Quick check of the rest of the line to find the next token.  If it
     begins with a `-', Posix says to not return `time' as the token.
     This was interp 267. */
      i = shell_input_line_index;
      while (i < shell_input_line_len && (shell_input_line[i] == ' ' || shell_input_line[i] == '\t'))
        i++;
      if (shell_input_line[i] == '-')
    return 0;
    }

  switch (last_read_token)
    {
    case 0:
    case ';':
    case '\n':
    case AND_AND:
    case OR_OR:
    case '&':
    case WHILE:
    case DO:
    case UNTIL:
    case IF:
    case THEN:
    case ELIF:
    case ELSE:
    case '{':       /* } */
    case '(':       /* )( */
    case ')':       /* only valid in case statement */
    case BANG:      /* ! time pipeline */
    case TIME:      /* time time pipeline */
    case TIMEOPT:   /* time -p time pipeline */
    case TIMEIGN:   /* time -p -- ... */
      return 1;
    default:
      return 0;
    }
#else
  return 0;
#endif /* COMMAND_TIMING */
}

You see, time can be followed by most others bash reserved words.

In case of external command, the normal rule was applied, { was considered input of /usr/bin/time. } alone is invalid token, and bash raise the error.


In:

/usr/bin/time echo hello

external time did not call the shell builtin echo but the external echo command.

A strace verifies that:

$ strace -fe execve /usr/bin/time echo 1
execve("/usr/bin/time", ["/usr/bin/time", "echo", "1"], [/* 64 vars */]) = 0
Process 25161 attached
....
[pid 25161] execve("/usr/bin/echo", ["echo", "1"], [/* 64 vars */]) = -1 ENOENT (No such file or directory)
[pid 25161] execve("/bin/echo", ["echo", "1"], [/* 64 vars */]) = 0
1
[pid 25161] +++ exited with 0 +++
....

Here external time lookup your PATH variable to find the command executable. That also explain in case of using a function, you got No such file or directory becasue there's no command named mytest in your PATH.

Related Question