Search code examples
bashshelloperators

|| operator ignored, both commands are executed


Been unsuccessful figuring out why the OR operator is ignored and both commands are executed.

find / -ipath /usr/bin 2>/dev/null -print -quit || echo "Not a valid directory"
/usr/bin
Not a valid directory

Been experimenting with command substitution and grouping but the result is the same or find is ignored.

{find / -ipath /usr/bin 2>/dev/null -print -quit || echo "Not a valid directory"}
Not a valid directory}
$(find / -ipath /usr/bin 2>/dev/null -print -quit) || echo "Not a valid directory"
-bash: /usr/bin: Is a directory
Not a valid directory

Solution

  • If we remove 2>/dev/null:

    For the first find command we may see something like:

    $ find / -ipath /usr/bin -print -quit
    find: ‘/lost+found’: Permission denied
    find: ‘/root’: Permission denied
    /usr/bin
    $ echo $?
    1
    $
    

    So we see that find continues running after errors, but returns a non-zero exit code at the end. So the "OR" would trigger and your echo would run.


    For the second case:

    $ {find / -ipath /usr/bin -print -quit
    sh: {find: not found
    $ echo $?
    127
    $
    

    This has attempted to run a comand called literally {find, which does not exist. This also returns a non-zero result. So the "OR" would trigger and your echo would run.

    Note that { is only special to the shell when it appears as a separate word at the start of a command; and } is only special after ;, a newline, or in a few other situations. Consider:

    {true || echo ok}
    
    { false || echo ok;}
    
    { false || echo ok
    }
    
    { false || echo ok}
    

    (Note that the fourth of these is still waiting for a terminating }.


    Finally, using $( ... ) causes the shell to execute the command inside the parentheses and then carry on as if its output were part of the original commandline. Consider:

    $ echo hello
    hello
    $ $(printf 'ec%co h%c' h e)llo
    hello
    $
    

    When you applied it to find, the shell attempted to use find's output as the command to run. /usr/bin is, as the error stated, a directory and not a program.

    It is more normal to use $(...) to produce arguments to a command rather than to generate the command itself.