Find: Why is the -a operator not commutative in combination with -print

find

Supposing we're using find in current directory with audio files (e. g. MP3) and (important!) also non-mp3 files (e. g. JPEG) present in current directory or somewhere below it.
Additionally to that, we have a directory in there with some other audio files
(called exclude_me in the example), which should live up to its name by being excluded from the file search:

find . \( -type d -name 'exclude_me' -prune \) -o \( -type f -a -iname '*.mp3' -a -print \)

(Though not absolutely necessary in this case, I've deliberately set some redundant parentheses, as well as additional -a operators for the sake of clarity.)

Because of the explicit -print option (cf. also http://mywiki.wooledge.org/UsingFind), this will indeed omit anything from "exclude_me" directory, and only list the *.mp3 files recursively from current directory.
Sometimes (not always) the alias command may already be sufficient for a find one-liner, but this command will require the argument at the end of the line.
Taking notice that the '*.mp3' argument to -iname is before the -print in this case, there appears to be no way to solve the problem with an alias.

But why not pretend as if in mathematics by simply swapping the two options?
Like this:

find . \( -type d -name 'exclude_me' -prune \) -o \( -type f -a -print -a -iname '*.mp3' \)

Remember I set a precondition to have at least one non-mp3 file (e. g. image) in the . directory or its subdirectories. It has been set to give a clearer proof that this does not work as expected under the hood. Without them, things will merely look as working fine. Having them present instead will cause the non-mp3 files to get listed as well despite the distinct -iname '*.mp3' specification in the one-liner.
So why doesn't the -a operator work like in mathematics, where "A + B" is the same as "B + A"? Or, in find terms:

\( -type f -a -print -a -iname '*.mp3' \)

ought to be the same as

\( -type f -a -iname '*.mp3' -a -print \)

or not?

Is there no way to get the desired result and have the -iname argument at the end of the line, not anywhere in between?
(except for ugly hackish solutions like grep -v exclude_me or the like)

Best Answer

and and or are not commutative in many programming languages e.g. C, C++, C♯, shells, find, and many more.

They use lazy left to right evaluated i.e. it evaluates the term on the left first, and then only evaluates term on right if it needs to. So in false and b, b is not evaluated as the answer is always false. But in b and false, b is evaluated, as it has not yet seen that the second term is false.

This does not make any difference when the terms are pure functions i.e. a predicate (something that returns a boolean and does nothing else), but when it does something (has side effects) then it does matter.

-print does something. It prints, therefore it matters.

Note, Any system that allows side effects (such as -print), must define the order of evaluation, and left to right is probably the simplest.

Related Question