Why does `find` in Linux skip expected results when `-o` is used?

I think once you used -or operator, then you've to keep it consistent in order to avoid ambiguous order of logical operations when you have multiple conditions connected using logical OR.

It seems the -exec part is grouped together with the second -name "*.h".

So in order to make it work correctly, you've to add the brackets as below:

find . '(' -name '*.cpp' -o -name '*.h' ')' -exec echo {} ';'

Remember: The parentheses must be quoted or escaped with a backslash to prevent them from being interpreted as special shell characters.

Alternatively combine few extensions into one by using -regex:

find . ! -regex ".*\.\(cpp\|h\)" -exec echo {} \;

Neither. It's the syntax of the options that is "wrong". find evalutates sequencially. Hence, it evaluates the first expression (-name "*.cpp") then encounters a -o flag. If the first expression is true, find will not continue evaluating the second one (-name "*.h" -exec echo {} \;), instead does nothing. You see, the whole part after -o is one expression. Therefore, this is only executed for files that match the second expression. That's why you see only the 1.h file, which passes only the second expression. See the find manpage:

   expr1 -o expr2
          Or; expr2 is not evaluated if expr1 is true.

Why is this useful? Consider the following:

find /path -exec test_file_for_something {} -print \; -o -name "xyz" -exec ls -l {} \;

In this find statement the file is given to test_file_for_something as a parameter. Now, depending on that commands return code the first expression is true (then -print is executed and it ends there) or false (then the second expression after the -o flag is evaluated). And if that one is true (the name is xyz), then -exec is executed.

For your problem you can instead use this to group the elements together as one expression:

find . \( -name "*.cpp" -o -name "*.h" \) -exec echo {} \;

Tags:

Linux

Find