bashfind

bash find directory/file in undetermined number of levels


In bash, I am trying to find directories containing a specific file; however, these directories are on different levels, eg, if I am looking for dir03/myfile.txt:

dir01/dir02/dir03/myfile.txt
dir01/dir03/ ##no target file in it
dir01/dir04/dir05/dir03/myfile.txt
dir01/dir04/dir05/dir06/myfile.txt ##I do not want this

I could do:

find ./dir01 -type -d -iname "dir03"

But then I would get all directories, even the ones without the target file

If I do this:

find ./dir01/*/dir03 -iname "myfile.txt" 

I would not see "dir01/dir04/dir05/dir03/myfile.txt"

This does not work:

find ./dir01 -iname "dir03/myfile.txt"

##find: warning: Unix filenames usually don't contain slashes (though pathnames do).  
That means that '-iname ‘dir03/myfile.txt’' will probably evaluate to false all the time on this system.  You might find the '-wholename' test more useful, or perhaps '-samefile'.  Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ ‘dir03/myfile.txt’'

I can do this:

find ./dir01 -iname "myfile.txt" -print0 | grep -FzZ 'dir03' | sed "s/myfile\.txt/\n/g"

It finds all the matches where myfile.txt is, and gets only the ones contained by the target directory; after that, I split the results with a new line (the line above gives the results concatenated, eg ./dir01/dir02/dir03/myfile.txt./dir01/dir04/dir05/dir03/myfile.txt But it might make the search more generic and longer in case I have a complex subdirectory structure. I was wondering if there was an easier solution to tell find to find "dir03/myfile.txt" at any level

Please note that the directory names are just for example sake, they do not share the same prefix.

Edit: I guess I could do

find ./dir01 -iname "myfile.txt" > myres0.txt
grep 'dir03' myres0.txt > myres1.txt

Considering that I am looking for multiple parental directories and not just dir03, it would do just one search, then I can use a txt file with my target directories and do:

grep -f mytargetDirs.txt myres0.txt > myres1.txt

Still wondering if there is a better way though.


Solution

  • Sounds like this is all you need:

    find ./dir01 -type f -name 'myfile.txt' | grep '/dir03/[^/]*$'
    

    Add NUL-termination options (-print0 and grep -Z) to protect against newlines in directory/file names as you see fit.