1

I'm getting a result from the unix 'find' command that I don't understand.

Using the Terminal on MacOS, which I believe is a POSIX-compliant os, running this shell script:

#!/bin/bash
#
echo "version 1" 
find . -iname "*tif" -o -iname "*foo"
#
echo "version 2" 
find . -iname "*tif" -o -iname "*foo" -print

I obtain the result:

version 1
./file1.tif
./file2.tif
./file3.tif
version 2
%

I expected to get the same output. Why does "print" with "or" suppress the output and cause nothing to be found?

1 Answers1

2

This command:

find . -iname "*tif" -o -iname "*foo"

is equivalent to:

find . \( -iname "*tif" -o -iname "*foo" \) -print

In other words the implicit -print is not mindlessly added to the end. It is added to the end after treating the explicit expression as a whole. This is clearly stated in the POSIX specification of find:

If no expression is present, -print shall be used as the expression. Otherwise, if the given expression does not contain any of the primaries -exec, -ok, or -print, the given expression shall be effectively replaced by:

( given_expression ) -print

In a shell you want to escape or quote each parenthesis, thus \( and \) in my code above. Also note your implementation of find may add something (e.g. -printf) to the list of primaries that suppress the implicit -print.

Your other command is:

find . -iname "*tif" -o -iname "*foo" -print

and it is equivalent to:

find . -iname "*tif" -o \( -iname "*foo" -a -print \)

The placement of parentheses is an implication of the fact -a (even if you omit it) binds "stronger" than -o.

Now the following fragment of the manual is relevant:

expression -o expression
Alternation of primaries; the OR operator. The second expression shall not be evaluated if the first expression is true.

In our case the second expression is the whole content of the parentheses. If for a given file -iname "*tif" evaluates as true then -iname "*foo" -a -print is not evaluated at all.

A somewhat surprising consequence of this "at all" is manifested in the following command with two occurrences of -iname "*tif":

find . -iname "*tif" -o -iname "*tif" -print

This command will never -print. For any file:

  • either the first -iname "*tif" is true and then everything after -o is not evaluated;
  • or the first -iname "*tif" is false and thus the second -iname "*tif" is evaluated and also false, so -print is not evaluated.

find . expression1 -o expression2 -print will -print a pathname if and only if expression1 evaluates as false and expression2 evaluates as true.

Similar question: Combining multiple find commands into one.