2

I'm using the find command to get a list of folders where certain files are located. But because of a permission denied error for certain subdirectories, I want to exclude a certain subdirectory name. I already tried these solutions I found here:

find /path/to/folders -path "*/noDuplicates" -prune -type f -name "fileName.txt"

find /path/to/folders ! -path "*/noDuplicates" -type f -name "fileName.txt"

And some variations for these commands (variations on the path name for example). In the first case it won't find a folder at all, in the second case I get the error again, so I guess it still tries to access this directory. Does anyone know what I'm doing wrong or does anyone have a different solution for this?

Mary
  • 65
  • 4
  • 1
    from http://stackoverflow.com/questions/4210042/exclude-directory-from-find-command , there is a `-o` option to add after `-prune`. Have you tried that ? – olivm Apr 20 '15 at 13:51
  • Thanks for your suggestion! I indeed tried that, apparently it means something like 'or', if you like to excluded multiple names/paths. – Mary Apr 20 '15 at 13:56

4 Answers4

4

To complement olivm's helpful answer and address the OP's puzzlement at the need for -o:

  • -prune, as every find primary (action or test, in GNU speak), returns a Boolean, and that Boolean is always true in the case of -prune.
  • Without explicit operators, primaries are implicitly connected with -a (-and), which, like its brethren -o (-or) performs short-circuiting Boolean logic.
  • -a has higher precedence than -o.

For a summary of all find concepts, see https://stackoverflow.com/a/29592349/45375

Thus, the accepted answer,

find . -path ./ignored_directory -prune -o -name fileName.txt -print

is equivalent to (parentheses are used to make the evaluation precedence explicit):

find . \( -path ./ignored_directory -a -prune \) \
       -o \
       \( -name fileName.txt -a -print \)

Since short-circuiting applies, this is evaluated as follows:

  • an input path matching ./ignored_directory causes -prune to be evaluated; since -prune always returns true, short-circuiting prevents the right side of the -o operator from being evaluated; in effect, nothing happens (the input path is ignored)
  • an input path NOT matching ./ignored_directory, instantly - again due to short-circuiting - continues evaluation on the right side of -o:
    • only if the filename part of the input path matches fileName.txt is the -print primary evaluated; in effect, only input paths whose filename matches fileName.txt are printed.

Edit: In spite of what I originally claimed here, -print IS needed on the right-hand side of -o here; without it, the implied -print would apply to the entire expression and thus also print for left-hand side matches; see below for background information.


By contrast, let's consider what mistakenly NOT using -o does:

find . -path ./ignored_directory -prune -name fileName.txt -print

This is equivalent to:

find . -path ./ignored_directory -a -prune -a -name fileName.txt -a -print

This will only print pruned paths (that also match the -name filter), because the -name and -print primaries are (implicitly) connected with logical ANDs;
in this specific case, since ./ignored_directory cannot also match fileName.txt, nothing is printed, but if -path's argument is a glob, it is possible to get output.


A word on find's implicit use of -print:

POSIX mandates that if a find command's expression as a WHOLE does NOT contain either

  • output-producing primaries, such as -print itself
  • primaries that execute something, such as -exec and -ok
  • (the example primaries given are exhaustive for the POSIX spec. of find, but real-world implementations such as GNU find and BSD find add others, such as the output-producing -print0 primary, and the executing -execdir primary)

that -print be applied implicitly, as if the expression had been specified as:

\( expression \) -print

This is convenient, because it allows you to write commands such as find ., without needing to append -print.

However, in certain situations an explicit -print is needed, as is the case here:

Let's say we didn't specify -print at the end of the accepted answer:

find . -path ./ignored_directory -prune -o -name fileName.txt

Since there's now no output-producing or executing primary in the expression, it is evaluated as:

find . \( -path ./ignored_directory -prune -o -name fileName.txt \) -print

This will NOT work as intended, as it will print paths if the entire parenthesized expression evaluates to true, which in this case mistakenly includes the pruned directory.

By contrast, by explicitly appending -print to the -o branch, paths are only printed if the right-hand side of the -o expression evaluates to true; using parentheses to make the logic clearer:

find . -path ./ignored_directory -prune -o \( -name fileName.txt -print \)

If, by contrast, the left-hand side is true, only -prune is executed, which produces no output (and since the overall expression contains a -print, -print is NOT implicitly applied).

Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
2

Following my previous comment, this works on my Debian :

find . -path ./ignored_directory -prune -o -name fileName.txt -print

or

find /path/to/folder -path "*/ignored_directory" -prune -o -name fileName.txt -print

or

find /path/to/folder -name fileName.txt -not -path "*/ignored_directory/*"

The differences are nicely debated here

Community
  • 1
  • 1
olivm
  • 106
  • 5
  • The first one did the trick! Thank you!! Apparently I don't understand -o correctly.. – Mary Apr 20 '15 at 14:23
  • 1
    the `-o` flag means that if the 2nd part of the expression (the `-name` condition and the `-print` action) is done only if the 1st part (the path condition) is false. – olivm Apr 20 '15 at 14:32
1

Edit (added behavior specification details)

Pruning all permission denied directories in find

Using gnufind.

Specification behavior details - in this solutions we want to:

  1. exclude unreadable directories contents (prune them),
  2. avoid "permission denied" errors coming from unreadable dierctory,
  3. keep the other errors and return states, but
  4. process all files (even unreadable files, if we can read their names)

The basic design pattern is:

find ...  \( -readable -o -prune \) ...

Example

find /var/log/ \( -readable -o -prune \) -name "*.1" 

\thanks{mklement0}

JJoao
  • 4,891
  • 1
  • 18
  • 20
  • 1
    @mklement0, Thank you once more!! Now I am happy! (r1) thank for `-print` in `-o` trick! (r2) I added "a intended behavior specification details" to my answer. As general conclusion `find` in general is fantastic for simple things but "tricky" for "tricky" problems... – JJoao Apr 21 '15 at 07:52
  • Thanks for encouraging me to do so, but which information do you mean, specifically? – mklement0 Apr 21 '15 at 13:15
  • some axiomatic translation of find args... `a -o b = (a orelse b) print` ; `a -o b print = a orelse (b print)` -- one of this days I will make a find-dsl :) – JJoao Apr 21 '15 at 13:22
  • 1
    OK, thanks; I've added a section to my answer; let me know if you think it needs improvement. – mklement0 Apr 21 '15 at 13:49
-1

The problem is in the way find evaluates the expression you are passing to the -path option. Instead, you should try something like:

find /path/to/folders ! -path "*noDuplicates*" -type f -name "fileName.txt"
Yannoff
  • 367
  • 2
  • 9
  • Thank you! Unfortunately that still gives the same error :( – Mary Apr 20 '15 at 13:58
  • Then maybe you could just "hide" the errors by redirecting standard error messages: `find /path/to/folders ! -path "*noDuplicates*" -type f -name "fileName.txt" 2>/dev/null` – Yannoff Apr 20 '15 at 13:59
  • Also already tried to do that, but than it will still fail (when using the command in a shell script).. I tested this command on a test directory, without the problem causing subdirectories and than it works fine, so I'm pretty sure that this is the problem. But thank you very much for your help! – Mary Apr 20 '15 at 14:09