TL;DR: list separators such as ;
. &
, &&
, and ||
decide the parsing order.
The bash manual tells us:
AND and OR lists are sequences of one or more pipelines separated by
the && and || control operators, respectively.
Or how Bash Hacker's wiki succinctly put it
<PIPELINE1> && <PIPELINE2>
Thus, in cd ~/screenshots/ && ls screenshot* | head -n 5
there is one pipeline - ls screenshot* | head -n 5
and one simple command cd ~/screenshots/
. Note that according to the manual
Each command in a pipeline is executed as a separate process (i.e.,
in a subshell).
On the other hand, (cd ~/screenshots/ && ls screenshot*) | head -n 5
is different - you have one pipeline: on the left there is subshell and on the right you have head -n 5
. In this case, using OP's notation it would be (A && B) | C
Let's take another example:
$ echo foo | false && echo 123 | tr 2 5
$
Here we have one list <pipeline1> && <pipeline2>
. Since we know that exit status of pipeline is the same as of the last command and false
returns negative status aka fail, &&
won't execute right hand side.
$ echo foo | true && echo 123 | tr 2 5
153
Here the left pipeline has success exit status, so right pipeline is executed and we see its output.
Note that shell grammar doesn't imply actual execution order. To quote one of Gilles's answer:
Piped commands run concurrently. When you run ps | grep …, it's the luck of the draw (or a matter of details of the workings of the shell combined with scheduler fine-tuning deep in the bowels of the kernel) as to whether ps or grep starts first, and in any case they continue to execute concurrently.
And from bash manual:
AND and OR lists are executed with left associativity.
Based on that in cd ~/screenshots/ && ls screenshot* | head -n 5
the cd ~/screenshots/
would be executed first, ls screenshot* | head -n 5
if previous command succeeds, but head -n 5
may be a first process spawned rather than ls
since they are in a pipeline.
|
is just a connector that fits the LHS stdout to the RHS stdin. So if thecd
command in your example fails, it would send no output tohead
, buthead
would in fact stillexecute
, parse the nothing, and return no output. – DopeGhoti Jan 29 '19 at 20:31bash
is using a yacc parser not some ad-hoc thing; if you run that throughyacc -v
it will give you iny.output
a nice grammar that shows that&&
and||
combine lists, and lists are eventually made up of pipelines (and not the reverse); tl;dr;A && B | C
is the same asA && { B | C; }
, as expected. Don't assume any order of execution betweenB
andC
; the commands in a pipeline are run in parallel. – Jan 30 '19 at 00:10[...]
tests and$((...))
arithmetic evaluations; in particular,||
and&&
as used with commands list in the shell language have the same precedence, unlike the respective operators inC
or in arithmetic evaluation (where&&
binds more tightly than||
). – Jan 30 '19 at 00:23