Search code examples
linuxfindgnu-findutils

GNU find: when does the default action apply?


The man page of Debian 8's find command says:

If the whole expression contains no actions other than -prune or -print, -print is performed on all files for which the whole expression is true.

So why do these outputs differ:

$ mkdir -p test/foo test/bar && cd test && touch foo/bar bar/foo
$ # Test 1
$ find . -name foo -type d -prune -o -name foo
./foo
./bar/foo
$ # Test 2
$ find . -name foo -type d -prune -o -name foo -print
./bar/foo

So test 1: does the expression contain "no actions other than -prune or -print?" Well, excluding the prune, yes that statement is true, there are no actions. So these results are expected since for ./foo the expression before the -o option returns True, so it's printed.

But test 2: does the expression contain "no actions other than -prune or -print?" Well, excluding the prune and the print, yes that statement is true again, there are no other actions. So I would expect the same results.

But I don't get ./foo. Why?

It's as if the man page should read: "If the whole expression contains no actions other than -prune or -print, -print is performed on all files for which the whole expression is true."


Solution

  • I'm going with the simpler explanation, the man page is wrong. It should instead say

    If the whole expression contains no actions other than -prune or -print, -print is performed on all files for which the whole expression is true.

    It should also maybe contain a caveat for -quit, which is an action, but it causes -find to exit immediately. So even though an implicit -print is added for the whole expression it is never actually executed.

    The posix find man page contains a clearer explanation, though it doesn't have quite as many actions as the expanded gnu version.

    If no expression is present, -print shall be used as the expression. Otherwise, if the given expression does not contain any of the primaries -exec, -ok, or -print, the given expression shall be effectively replaced by:

    ( given_expression ) -print

    Out of what gnu calls actions, posix only defines -exec, -ok, -print, and -prune. It does not have any of the expanded actions -delete, -ls, etc... So the definition matches the corrected gnu one by only omitting -prune.

    Here are some examples using all the gnu find actions which prove the point. For all consider the following file structure

    $ tree
    .
    └── file
    

    -delete

    $ find -name file -delete
    $
    

    -exec command ;

    $ find -name file -exec echo '-exec is an action so an implicit -print is not applied' \;
    -exec is an action so an implicit -print is not applied
    $
    

    -execdir command {} +

    $ find -name file -exec echo 'This should print the filename twice if an implicit -print is applied: ' {} +
    This should print the filename twice if an implicit -print is applied:  ./file
    $
    

    -fls

    $ find -name file -fls file
    $
    

    -fprint

    $ find -name file -fprint file
    $
    

    -ls

    $ find -name file -ls
    1127767338    0 -rw-rw-r--   1 user   user          0 May  6 07:15 ./file
    $
    

    -ok command ;

    $ find -name file -ok echo '-ok is an action so an implicit -print is not applied' \;
    < echo ... ./file > ? y
    -ok is an action so an implicit -print is not applied
    $
    

    -okdir command ;

    $ find -name file -okdir echo '-okdir is an action so an implicit -print is not applied' \;
    < echo ... ./file > ? y
    -okdir is an action so an implicit -print is not applied
    $
    

    -print

    #./file would be printed twice if an implicit `-print was applied`
    $ find -name file -print
    ./file
    $
    

    -print0

    #./file would be printed twice if an implicit `-print was applied`
    $ find -name file -print0
    ./file$
    

    -printf

    $ find -name file -printf 'Since -printf is an action the implicit -print is not applied\n'
    Since -printf is an action the implicit -print is not applied
    $
    

    -prune

    $ find -name file -prune
    ./file
    $
    

    -quit

    $ find -name file -quit
    $ find -D opt -name file -quit
    ...
    Optimized command line:
    ( -name file [0.1] -a [0.1] -quit [1]  ) -a [0.1] -print [1]