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."
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]
Let's look at this command:
find . -name foo -type d -prune -o -name foo
Since -print
is the default action, then this action is applied to the whole set of expressions, i.e. -name foo -type d -prune -o -name foo
. So it's the same as the following:
find . \( -name foo -type d -prune -o -name foo \) -print
Now let's look at this command:
find . -name foo -type d -prune -o -name foo -print
According to man find
expr1 expr2
has higher priority than expr1 -o expr2
. So in the command above two expressions are combined with the OR operator:
-name foo -type d -prune
-name foo -print
So if you want to apply -print
to both, use parentheses:
find . \( -name foo -type d -prune -o -name foo \) -print
But -prune -o RHS
implies that RHS
is evaluated only for those items which didn't get pruned.
We can check if we are right by running find
with -D tree
or -D opt
:
find -D opt -O0 . -name foo -type d -prune -o -name foo -print
...
( ( -name foo [0.1] -a [0.04] [need type] -type d [0.4] ) -a [0.04] [call stat] [need type] -prune [1] ) -o [0.14] ( -name foo [0.1] -a [0.1] -print [1] )
./bar/foo
find -D opt -O0 . -name foo -type d -prune -o -name foo
( ( ( -name foo [0.1] -a [0.04] [need type] -type d [0.4] ) -a [0.04] [call stat] [need type] -prune [1] ) -o [1] -name foo [0.1] ) -a [0.14] -print [1]
./foo
./bar/foo
As we can see, find
makes (... -prune) -o (... -print)
from the first expression where we put -print
explicitly. It makes (...) -a -print
from the second expression where we omit -print
.
So I think that by "the whole expression" the man page means one of expression parts described in OPERATORS
section.
Check the GNU Findutils manual, it says
If the expression contains no actions other than ‘-prune’, ‘-print’ is
performed on all files for which the entire expression is true.
Apparently, debian's manual is wrong, because it's just a GNU Find. And I have no idea why this happened, since it's just a copy to me.