In the command
command1 && command2 >file
the output of command1
is not redirected, but that of command2
is:
$ echo hello && echo ok >file
hello
$ cat file
ok
Redirecting command1
can be done separately:
command1 >file1 && command2 >file2
In the command
command1 | command2 && command3 | command4
the output of command1
is piped to command2
. If the first pipeline terminates with a zero exit status, the second pipeline is run in a similar way:
$ echo hello | cat && echo bye | cat
hello
bye
If the command2 && command3
list was to be grouped, then write it as
command1 | { command2 && command3; } | command4
which means the output of command1
is piped to the compound command command2 && command3
. The output of the compound command is then piped to command4
:
$ echo hello | { read message && printf 'We got "%s"\n' "$message"; } | rev
"olleh" tog eW
Individual simple commands (see below) may be redirected:
$ echo hello | { read message && printf 'We got "%s"\n' "$message"; echo bye >&2; } | rev
bye
"olleh" tog eW
In the shell grammar, a "complete command" is made up of a list of pipelines separated by &&
or ||
. This is very loosely speaking. This means that &&
and ||
will have higher precedence than the |
in a pipeline.
The redirection, on the other hand, binds quite tightly to the current command as the grammar makes the redirection part of a "simple command" construct. A simple command is some command prefix, the name of a command, and a command suffix (where prefix and suffix are optional). A command prefix may be an assignment to an environment variable (VAR=value myscript
), or a redirection (>outfile cat
). Likewise a command suffix may be a redirection (cat >outfile
), etc.
A "compound command" can also be redirected, obviously. A compound command is a pipeline (possibly a single simple command) in a { ...; }
brace group, or in a ( ... )
subshell, or an if
, while
, for
, until
, or a case
statement.
The full grammar for the POSIX shell (which bash
expands upon) is available in the POSIX standard. The following is just the top-level of the grammatical rules:
program : linebreak complete_commands linebreak
| linebreak
;
complete_commands: complete_commands newline_list complete_command
| complete_command
;
complete_command : list separator_op
| list
;
list : list separator_op and_or
| and_or
;
and_or : pipeline
| and_or AND_IF linebreak pipeline
| and_or OR_IF linebreak pipeline
;
pipeline : pipe_sequence
| Bang pipe_sequence
;
pipe_sequence : command
| pipe_sequence '|' linebreak command
(Reference: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_10_02)