Why does the sequence matter in the execution of these bash commands?
There seems to be some inconsistency that I am not able to understand regarding the bash shell.
If I execute:
ls;date;time
the results of the three queries are shown in sequence.
However, on interchanging date and time position, an error message pops up.
So if I execute:
ls;time;date
the error message says: bash: syntax error near unexpected token 'date'
.
Can someone explain this?
Solution 1:
The time
command in your pipeline is not the /usr/bin/time
binary, but the bash time
built-in. Compare man time
with help time
. The error you see is bash failing to parse time
's argument. This must either be present or be a newline. It is a newline in your first example but absent in the second.
On the other hand, if you were to run
ls;date;'time'
or
ls;'time';date
where the quotes around 'time'
revoke its status as a reserved word, then bash has no problems parsing the line. It now parses three commands in a list, which it will execute in sequence, and /usr/bin/time
will report a usage error in either case.
Addendum
It was observed that though time ; date
yields an error, time ; ; date
does not. The likely explanation is that time ;
is interpreted by bash as equivalent to time <newline>
. The expression time ; ; date
is then parsed as the list of time ;
and date
.
This is consistent with the observation that time ;
and time ; ;
are legal as well, the second being parsed as the singleton list containing time ;
followed by the optional semicolon allowed after lists.
So another way of explaining why time ; date
yields the error bash: syntax error near unexpected token 'date'
is that time
consumes the semicolon separating it from date
. It can only do that because time
is a bash reserved word.
Solution 2:
Bash treats the built-in time
as a special case, when parsing command-lines.
As can be read in the bash manpage, the line as typed is first split into a list:
pipeline ; pipeline
where a pipeline is:
[time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]
or in our case, simply:
time command
i.e. if time is present, then command must also be present.
[There is a special case that allows time
to be followed by a newline, but that doesn't apply here]
So, in our case, we have:
time;date
being split into two pipelines:
1. time
2. date
and pipeline 1 is not well formed, since we have time
without a command. Hence the error.
Note that the command-line time
doesn't work here either:
$ /usr/bin/time;date
Usage: /usr/bin/time [-apvV] [-f format] [-o file] [--append] [--verbose]
bash parses this as expected, into 2 pipelines:
1. /usr/bin/time
2. date
and /usr/bin/time
then refuses to run with no argument. Note that this is an error from /usr/bin/time
not an error from bash.
The reason that back-tick works is that the back-tick stops time
being interpreted as a special element within the pipeline.
i.e. with the back-tick:
`time`;date
it is parsed as two pipelines:
1. `time`
2. date
Remember that a pipeline, in our case, is:
[time] command
and the problem initially was that we had time
with no command, which isn't allowed. But now we simply have the command:
`time`
without the preceding time
, since the back-ticks mean that time
is interpreted as the command, not as a preceding word.
So bash then runs its builtin time
with no args, which is accepted. It produces no output, and we see no error.
Note that:
`time`
actually runs the result of the time
built-in, i.e. it runs whatever the time
built-in produces on stdout. But since time
on its own doesn't write anything to stdout, it appears to work.
Finally, it's been noted that this works:
time ; ; date
which I can't explain, sadly :)