5

As most people know, the shell allows you to redirect stdin/stdout/stderr when running a command, and also to pipe output from one command to another.

As fewer people may know, you can write "lists" commands that get executed one after another, conditionally or unconditionally, using the ; && || operators.

How do these features interact? If I do something like

command1 && command2 >file

does it redirect the output of just the last command, or both of them? If I write

command1 | command2 && command3 | command4

what does it actually do? Is this two pipelines with a conditional? Or is this one pipeline that contains a conditional as one of the steps of the pipe?

As far as I'm aware, the shell doesn't support adding brackets to disambiguate, so I'm not sure how you'd request one interpretation or the other... Regardless, it would be useful to know how the shell interprets all this. (Like most humans, I only use Bash.)

7

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)

4
  • 1
    Is there any place where those rules are written down? For example in man bash? Just to have a general rule – Francesco Lucianò Jun 15 '20 at 12:09
  • 1
    @FrancescoLucianò See updated answer. – Kusalananda Jun 15 '20 at 12:14
  • 1
    Studying this closely, if I'm reading this right: Variable assignments and stream redirections have highest priority, then |, then !, then && and || have equal priority, and finally ; and & have lowest priority. I think...(!) – MathematicalOrchid Jun 15 '20 at 12:41
  • @MathematicalOrchid I would say so, yes. Roughly speaking. – Kusalananda Jun 15 '20 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.