skip to Main Content

I have few files under the current directory,

./a.txt
./b.txt
./dir1/c.txt
./dir1/d.txt

When I execute find . -path './dir1' -prune -o -name "*.txt",

as expected, it excludes everything under ‘dir1’ and prints,

./a.txt
./b.txt
./dir1

now if I switch the sequence of the subexpression and execute find . -name "*.txt" -o -path './dir1' -prune,
to my understanding, it should have displayed all the files, as -name "*.txt" subexpression should have matched everything that has .txt extension and just or them with the following test. Therefore ‘dir1’ directory contents should not have been excluded. But that’s doesn’t seem to be the case. Displayed result is same as the previous one.

./a.txt
./b.txt
./dir1

what am I missing here?

My system is ubuntu-20.04. I have skim through the find man page and couldn’t find any solution.
Other sources in the web didn’t provide with any satisfactory explanation either.

3

Answers


  1. To be honest, in 40+ years of shell programming I don’t think I’ve never used the -prune option for find as I find (no pun intended) it too confusing and so end up restricting my search path or discarding unwanted results in other ways.

    In general, though, A or B = B or A, e.g. "I want a cat or a dog" = "I want a dog or a cat", so when "A" = -path './dir1' -prune, "B" = -name "*.txt" and "or" = -o, these are equivalent:

    1. A (-path './dir1' -prune) or (-o) B (-name "*.txt")
    2. B (-name "*.txt") or (-o) A (-path './dir1' -prune)
    Login or Signup to reply.
  2. The -o is not applied in a complete run of find (ie, ‘do all the things on the LH side of -o and if nothing is found then do the RH side…)

    Instead the -o is file by file which is a big difference. Essentially you have this loop:

    1. Find a file;
    2. apply all the expressions to that file;
    3. IF the overall expression is true, execute the action (which default is print)

    The other thing going on here is no combination of -o‘s or -a‘s or parenthesis will overcome that -prune has the highest precedence — if a directory is listed with -prune it is never visited.

    If you want to do something along these lines:

    find_like . LH_do_first --or RH_do_if_LH_finds_nothing
    

    Then Bash is probably your best bet. Something along these lines:

    lh=$(find . -name "*.txt")
    cnt=$(awk '/[^[:blank:]]/{cnt++} END {print cnt}' <<<"$lh")
    if (( cnt > 0 )); then 
        printf "%s" "$lh"
    else
        find . -path "./dir1" -prune
    fi    
    
    Login or Signup to reply.
  3. You are assuming -prune means "do not show this entry".

    Instead, -prune means "do not descend into this directory".

    Your top level directory contains the following:

    ./a.txt
    ./b.txt
    ./dir1
    

    If you use -prune when you encounter ./dir1, then find will not descend into it and therefore never look at ./dir1/c.txt or ./dir1/d.txt.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search