问题
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."
回答1:
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
$
#./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]
回答2:
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.
回答3:
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.
来源:https://stackoverflow.com/questions/37047322/gnu-find-when-does-the-default-action-apply