4

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.

11

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.

| improve this answer | |
  • 1
    Nice explanation. I'd suggest putting the TL;DR part first, but you have my vote either way. – sa289 Jun 15 '15 at 16:38
9

The order of find arguments matters. The command needs to be constructed as find -type d -print0 and then it will work as expected. I just thought I'd post this in case it's helpful to anyone.

| improve this answer | |
  • 2
    As to why order matters, this might be of interest – iruvar Jun 14 '15 at 3:27
  • 1
    @1_CR; I skimmed through the linked document but couldn't exactly find the explanation why find -print -type d will return all files, given that the default logical connector is -a (AND), and type -d should thus also be considered. - My suspicion is that -print (always TRUE) will be effective before the whole logical expression is completely evaluated (which I feel to be counter-intuitive to the concept of logical expressions). – Janis Jun 14 '15 at 5:21
3

The trick to understand a find command is to recursively evaluate and group into the resulting logic value's appropriate expression (-true or -false) the first two expressions / actions, considering that the evaluation of each pair of expressions / actions is short-circuited (hence expressions / actions as the second operand of an AND comparison will not be evaluated / evaluated and executed if the first evaluated expression is FALSE and expressions / actions as the second operand of an OR comparison will not be evaluated / evaluated and executed if the first evaluated expression is TRUE) and that every action encountered during the process will be always evaluated as TRUE and executed at the time of its evaluation (if not skipped by a short-circuit evaluation) based on the logic value of the previously evaluated (and at the time of the evaluation already reduced to a -true or -false expression) pair of expressions / actions.

Expanding an example in the useful link posted by 1_CR:

find . -false -o -false -a -printf 'nope\n' -o -printf 'yep\n' -o -printf 'nope\n'
yep
  • -false -o -false: the first operand is an expression, so no action is executed, and its value is FALSE; the evaluation is not short-circuited, since <expr1> is FALSE, so -false is evaluated, and its value is FALSE; the whole expression's value is FALSE, and this is reduced to a -false expression which will be evaluated with the following expression / action;
  • -false -a -printf 'nope\n': the first operand is an expression, so no action is executed, and its value is FALSE; the evaluation is short-circuited, since <expr1> is FALSE, so <expr2> is not evaluated and not executed; the whole expression's value is FALSE, and this is reduced to a -false expression which will be evaluated with the following expression / action;
  • -false -o -printf 'yep\n': the first operand is an expression, so no action is executed, and its value is FALSE; the evaluation is not short-circuited, since <expr1> is FALSE, so printf 'yep\n' is evaluated and executed now, and its value is TRUE; the whole expression's value is TRUE, and this is reduced to a -true expression which will be evaluated with the following expression / action;
  • -true -o -printf 'nope\n': the first operand is an expression, so no action is executed, and its value is TRUE; the evaluation is short-circuited, since <expr1> is TRUE, so <expr2> is not evaluated and not executed; the whole expression's value is TRUE;

Doing the same on find [...] -print0 -type d, which expands to find [...] -print0 -a -type d (since a missing operator expands to an AND operator):

  • -print0 -a -type d: the first operand is an action, so -print0 is evaluated and executed now, and its value is TRUE; the evaluation is not short-circuited, since <expr1> is TRUE, so -type d is evaluated, and its value is either TRUE or FALSE; the whole expression's value is either TRUE or FALSE based on the -type d test's result;
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.