bash - find: guaranteed order of -exec; and -print - Stack Overflow

admin2025-04-27  3

Say I do:

find <paths> -exec cmd '{}' \; -printf <format>;

Where:

  • -exec cmd '{}' prints exactly one line.
  • -printf <format> prints exactly one line.

Is the ordering of the output guaranteed, to be interleaved as follows?

exec line
printf line
exec line
printf line
exec line
printf line
...

That is:

  • Does find guarantee that the -exec; and printf tests not be reordered?
  • Does find guarantee that the individual tests not be batched, so I won't have a run of -exec; output followed by a run of -printf output?

The same question applies to the opposite order, with the output correspondingly adjusted:

find <paths> -printf <format> -exec cmd '{}' \;;

The manual does have the following to say:

GNU find
searches the directory tree rooted at each given starting-point
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.
-Olevel
        Enables query optimisation.  The find program reorders
        tests to speed up execution while preserving the overall
        effect; that is, predicates with side effects are not
        reordered relative to each other.  The optimisations
        performed at each optimisation level are as follows.

        0      Equivalent to optimisation level 1.

        1      This is the default optimisation level and
               corresponds to the traditional behaviour.
               Expressions are reordered so that tests based only
               on the names of files (for example -name and
               -regex) are performed first.

The first part seems to rule out batching and I assume that -exec; and -printf are considered to have side-effects, but nonetheless I would like explicit confirmation despite all my testing.

Say I do:

find <paths> -exec cmd '{}' \; -printf <format>;

Where:

  • -exec cmd '{}' prints exactly one line.
  • -printf <format> prints exactly one line.

Is the ordering of the output guaranteed, to be interleaved as follows?

exec line
printf line
exec line
printf line
exec line
printf line
...

That is:

  • Does find guarantee that the -exec; and printf tests not be reordered?
  • Does find guarantee that the individual tests not be batched, so I won't have a run of -exec; output followed by a run of -printf output?

The same question applies to the opposite order, with the output correspondingly adjusted:

find <paths> -printf <format> -exec cmd '{}' \;;

The manual does have the following to say:

GNU find
searches the directory tree rooted at each given starting-point
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.
-Olevel
        Enables query optimisation.  The find program reorders
        tests to speed up execution while preserving the overall
        effect; that is, predicates with side effects are not
        reordered relative to each other.  The optimisations
        performed at each optimisation level are as follows.

        0      Equivalent to optimisation level 1.

        1      This is the default optimisation level and
               corresponds to the traditional behaviour.
               Expressions are reordered so that tests based only
               on the names of files (for example -name and
               -regex) are performed first.

The first part seems to rule out batching and I assume that -exec; and -printf are considered to have side-effects, but nonetheless I would like explicit confirmation despite all my testing.

Share Improve this question asked Jan 11 at 15:57 user19087user19087 2,0931 gold badge17 silver badges25 bronze badges 6
  • 8 -printf is only evaluated if cmd succeeds so yes, the order is guaranteed. The manual doesn't promise a flush after -printf but says \c in the format string forces it so use that if you're paranoid. – oguz ismail Commented Jan 11 at 16:31
  • You've quoted from the manual page for GNU find, and -printf is a GNU extension. Are you then asking specifically about the GNU implementation? – John Bollinger Commented Jan 12 at 20:41
  • @oguzismail That's a great point! The command executed likely inherits it's descriptors from find, rather than piping it's output back for find to merge with -print. – user19087 Commented Jan 13 at 2:12
  • @JohnBollinger If that's the version of find found on the major Linux distributions: Arch, Nix, Fedora, Ubuntu, etc. – user19087 Commented Jan 13 at 2:13
  • I want to clarify that for this question, I assume the command to -exec will always return true (0). – user19087 Commented Jan 13 at 2:18
 |  Show 1 more comment

2 Answers 2

Reset to default 4

Is the ordering of the output guaranteed, to be interleaved as follows?

For GNU find, the manual specifies:

GNU find searches the directory tree rooted at each given starting-point by evaluating the given expression from left to right, according to the rules of precedence [...] 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.

That wording is a bit awkward, but it nevertheless conveys that

  1. the expression given in the find command is fully evaluated for each file considered before it is evaluated for the next file, one file at a time. Thus, find will not gang together multiple executions of the -exec or of the -printf.
  2. the expression is evaluated left-to-right, so for each file, the -exec will be performed first.
  3. However, evaluation short circuits. Your -exec and -printf operands are joined by an implicit and operator, so the exit status of the -exec'd command matters, because it determines whether the -exec evaluates to true (status 0) or false (any other exit status). If that command ever exits with non-zero status then the -printf will not be evaluated for that file.

Thus, in GNU find, the -exec and -printf are guaranteed to be evaluated in interleaved fashion, exec first, corresponding to the output example, provided that the -exec's command always terminates with status 0. I'll omit the details, but the same applies to POSIX find.

Edit: Since you mention test optimization in the question, the point and effect of GNU find's optimizations is to speed overall execution of find without changing the observable behavior, which definitely includes the order of any output produced by the evaluation of the operands of the find expression. This is essentially the same "as if" rule that commonly applies to optimization: the observable behavior is as if no optimization were performed, except that it may be produced faster. The manual excerpt you present goes on to specify that it is the evaluation of tests based only on file name that may be done early. That should be read exclusively: optimization will reorder only name-based tests relative to other operands of the expression.

It may be of interest to you that neither POSIX nor GNU explicitly specifies that the output associated with evaluating those operands will be emitted in the same order that they are evaluated, so the output order is not guaranteed in that sense. Nevertheless, it is indeed the expected, well known, and reliable behavior of all find implementations I know that the output associated with evaluating operands is indeed written in the same order that the operands are evaluated.

If you are so concerned with the order, and as the standards don't specify it, do it yourself

#! /bin/bash
# before
f="$1"
printf '%s\n' "$f"; cmd "$f"

or

#! /bin/bash
# after
f="$1"
cmd "$f" && printf '%s\n' "$f"

so

find <paths> -exec before '{}' \;

and

find <paths> -exec after '{}' \;

will behave as you expect.

转载请注明原文地址:http://anycun.com/QandA/1745705289a91076.html