8

I know of the relative binding precedence of operators ‘;’, ‘&’, ‘&&’, or ‘||’

http://www.gnu.org/software/bash/manual/bashref.html#Lists

but when pipes come into the picture along with '&&' I struggle to understand the binding strength and either stumble upon a correct command or just give up.

What is the binding precedence of '|' and '>' compared to the above?

Example where I get confused:

ls _thumbnails/video.mp4.jpg 2>/dev/null 
    && echo "thumbnail already generated. Not regenerating" \
    && exit \
    || ffmpeg_thumbnail_create video.mp4 2>/dev/null \
    && ls _thumbnails/video.mp4.jpg \
    && echo "Thumbnail successfully created" \
    && exit \
    || echo "Thumbnail creation failed" \
    | tee ~/thumbnails.log

The goal of the above is to create a thumbnail if and only if one isn't already present (I run a daily cronjob). And I don't like ffmpeg's huge amount of output when there is no error (which is not the Unix way). There are other situations too so don't start giving me advice that uses separate statements or special options specific to these programs. I want to understand binding precedence.

  • Very helpful - I'd mark this correct if it was a response. Just one more question - how do I alter precedence? In theory I think {...} should work. I might have tried it unsystematically and not got the expected results. – Sridhar Sarnobat Oct 22 '14 at 0:27
12

The short answer is that <, >, and their variants have the highest binding precedence (tightest binding), followed by |, followed by && and ||, followed by ; and &.  So, only the echo "Thumbnail creation failed" is piped into the tee.

A slightly longer answer would point out that the highest precedence is actually grouping, which can be indicated with parentheses or braces.  For example,

A  &&  (B; C)

and

A  &&  { B; C;}

are approximately equivalent to

if A
then
    B
    C
fi

Notes:

  • The parentheses give you a subshell; i.e., commands B and C run in a child process.  Therefore, commands like variable assignments or cd will have no effect on the parent shell.  Commands in braces run in the same process as the A command.  Therefore, the brace construct A && { B; C;} is closer to the if-then-else construct.
  • In the brace syntax, there must be a space after the { and a ; (or a &, or newline) before the }.

For further reading, see What are the shell’s control and redirection operators? and When is ‘if’ not necessary? (particularly my answers).

For even more further reading, see the bash(1) man page and the POSIX specification / definition of the Shell Command Language, specifically Section 2.9, Shell Commands and Section 2.10.2, Shell Grammar Rules.  This is an attempt to provide some context for the above:

  • Things like

    • myVar=42
    • IFS= read a
    • date
    • cd /some/directory
    • ls -laR dir1 dir2
    • cat foo* > /tmp/allfoo
    • ls -laR dir{1,2}
    • find . -type f -name "foo*" -print > /tmp/output 2> /dev/null
    • > newfile
    • [ -f catfood ]
    • exit

    are all considered to be “simple commands”.

  • Things like
      simple_command1  |  simple_command2  |  simple_command3
    are “pipelines”.  The grammar establishes building blocks and builds on them, as is typical for formal grammars like this (and for programming languages like C), so an individual “simple command” is considered to be a “pipeline”, even though it doesn’t contain a pipe.  It doesn’t make (semantic) sense for a variable assignment to be a component of a pipeline, but things like x=1 | od -ab or ls -laR | z=0 are syntactically valid.
  • Things like
      pipeline1  &&  pipeline2  ||  pipeline3
    are called “lists” by bash and “AND-OR lists” by POSIX.  Again, an individual “pipeline” or even an individual “simple command” is considered to be an “AND-OR list”, even though it doesn’t contain an AND or an OR.
  • When you get to things like
      AND-OR list1 &  AND-OR list2 ;  AND-OR list3
    the nomenclature starts to get a bit inconsistent.  Bash calls these “lists” also; POSIX calls them “lists”, “compound lists” and (rarely) “terms”.  Again, an individual “AND-OR list”, “pipeline” or even an individual “simple command” is considered to be a “list”, even though it doesn’t contain a & or a ;.
  • Things like
      (compound_list)
    and
      { compound_list;}
    and the flow-control commands (for, if-then-else, while, etc.) are called “compound commands”. 

In the examples above, it probably makes the most sense to interpret A, B, and C to be pipelines.  Remember, a “pipeline” can be an individual “simple command”; it doesn’t need to contain a pipe.

  • 1
    Thanks for the detailed explanation on the role of (..) and {..}. I had concluded from previous bad experience that round brackets were only used in the context of "dynamic commands" (i.e. $(..)) since I could barely get them to do anything as far as associativity was concerned. – Sridhar Sarnobat Oct 22 '14 at 17:56

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.