Other answers concentrate on the behavior of -o
and they are right, but this is only half of the story. The other half is the behavior of the implicit -print
.
In some circumstances find
adds an implicit -print
to the expression. This happens when the expression contains neither -exec
, -ok
, nor (explicit) -print
nor -print0
. Depending on the implementation of find
there may be other primaries that suppress the implicit -print
(e.g. -execdir
). An expression
that triggers this behavior makes find
convert a command like:
find paths expression
into:
find paths \( expression \) -print
(where backslashes are to protect (
and )
from being interpreted by your shell). The parentheses are there, this is how the implicit -print
works.
-name "*.cpp" -o -name "*.h"
you used is such expression
, so your:
find . -name "*.cpp" -o -name "*.h"
is equivalent to:
find . \( -name "*.cpp" -o -name "*.h" \) -print
If not the feature of the implicit -print
, your find
command without -exec
would print nothing at all. It's the implicit -print
that makes find
print what you expect in this case.
Now if you want to use -exec …
instead of the implicit -print
then you need to keep the parentheses and type them explicitly:
find . \( -name "*.cpp" -o -name "*.h" \) -exec echo {} \;
Your original command with -exec
missed these parentheses. This is the moment when the behavior of -o
matters. Yes, the parentheses are important when you use -o
, this is why you need them in the case with -exec
. You don't need to type them in the case without -exec
because they appear in the right places automatically due to the implicit -print
.