Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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."

like image 599
artfulrobot Avatar asked May 05 '16 09:05

artfulrobot


People also ask

Which is default action associated with find command?

The Default Action - Print Due to the fact that most people use find to display search results to the screen, the default action performed by find when none is specified, is "-print".

How does Linux find work?

The find command takes a number of paths, and searches for files and directories in each path “recursively”. Thus, when the find command encounters a directory inside the given path, it looks for other files and directories inside it.

How do I search for a specific file in Linux?

Use grep to Find a File in Linux Based on Content This searches every object in the current directory hierarchy ( . ) that is a file ( -type f ) and then runs the command grep "example" for every file that satisfies the conditions.


2 Answers

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]
like image 139
Reinstate Monica Please Avatar answered Oct 18 '22 08:10

Reinstate Monica Please


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.

like image 39
Ruslan Osmanov Avatar answered Oct 18 '22 07:10

Ruslan Osmanov