Find Command Returns Different Results with -print0

find

When doing a search like find -type d, adding the -print0 argument right after the find command such as find -print0 -type d causes the search to return more results than without it.

Best Answer

If you understand the && and || operators in the shell (and also in C, C++, and derivative languages), then you understand -a and -o in find.

To refresh your memory:

In the shell,

command1  &&  command2

runs command1, and, if it (command1) succeeds, it (the shell) runs command2.

command1  ||  command2

runs command1, and, if it (command1) fails, it (the shell) runs command2.

In the compilable languages,

expr1  &&  expr2

evaluates expr1.  If it (expr1) evaluates to false (zero), it returns that as the value of the overall expression.  Otherwise (if expr1 evaluates to a true (non-zero) value), it evaluates expr2 and returns that as the value of the overall expression.

expr1  ||  expr2

evaluates expr1.  If it (expr1) evaluates to a true (non-zero) value, it returns that as the value of the overall expression.  Otherwise (if expr1 evaluates to false (zero)) it evaluates expr2 and returns that as the value of the overall expression.

    This is known as “short-circuit evaluation”, in that it allows the evaluation of a Boolean expression without evaluating terms whose values are not needed to determine the overall value of the expression.

Quoting from find(1),

GNU find searches the directory tree rooted at each given file name by evaluating the given expression from left to right, according to the rules of precedence (see section OPERATORS), until the outcome is known (the left hand side is false for and operations, true for or), at which point find moves on to the next file name.
            ⋮

EXPRESSIONS

The expression is made up of … tests (which return a true or false value), and actions (which have side effects and return a true or false value), all separated by operators.  -and is assumed where the operator is omitted.
                    ⋮
    The subsection on ACTIONS states that -print, like
    most of the actions, always returns a value of true.
                    ⋮

OPERATORS

            ⋮
expr1 expr2
expr1 -a expr2
expr1 -and expr2    ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ not POSIX compliant
    And; expr2 is not evaluated if expr1 is false.
expr1 -o expr2
expr1 -or expr2    ⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ not POSIX compliant
    Or; expr2 is not evaluated if expr1 is true.

The Open Group Specification for find has similar things to say:

The find utility shall recursively descend the directory hierarchy …, evaluating a Boolean expression composed of the primaries described in the OPERANDS section for each file encountered.
            ⋮

OPERANDS

            ⋮
-print
    The primary shall always evaluate as true; it shall cause the current pathname to be written to standard output.
            ⋮
The primaries can be combined using the following operators (in order of decreasing precedence):
            ⋮

expression [-a] expression
    Conjunction of primaries; the AND operator is implied by the juxtaposition of two primaries or made explicit by the optional -a operator.  The second expression shall not be evaluated if the first expression is false.
expression  -o  expression
    Alternation of primaries; the OR operator.  The second expression shall not be evaluated if the first expression is true.

Both documents say, “If no expression is present, -print shall be used as the expression.”

---------------- TL;DR ----------------

So,

find -type d

is equivalent to

find -type d -print

which is equivalent to

find -type d -a -print

which means,

  • for each file,
    • evaluate the -type d test.
    • If it is true (i.e., if the current “file” is a directory), evaluate (perform) the -print action.

Whereas,

find -print -type d

is equivalent to

find -print -a -type d

which means,

  • for each file,
    • evaluate (perform) the -print action (i.e., this happens for all files).
    • If it is true (which -print always is), evaluate the -type d test.
    • And, since that’s the end of the command, the result of the -type d test is ignored.

So there you have it.

Related Question