In a BASH shell, I would like to take the lines of a file (eg pattern.txt) and find the files on my system whose names contain the patterns in each line of my file. So, I have the following for loop

for pp in `cat pattern.txt`; do find ./ -iname "*${pp}*" -print0; done

which doesn't find any files when if the first line in pattern.txt doe exist. So how can I fix the above command line?

Note: Each line in my file contains characters [a-zA-Z] only.

  • do the lines in pattern.txt contain wildcards? It would help if you listed a few lines – SiegeX Apr 1 '11 at 21:31
  • @SiegeX The lines in my file do not contain wildcards. – Azim Apr 1 '11 at 21:33
  • 2
    Could pattern.txt be a DOS/Windows text file, which to unix tools looks like each line ends with a CR character? If so, this command would look for files with a CR in their name. Run cat -A pattern.txt to check that it does indeed contain only letters (you should see lines like foobar$, nor foobar^M$). – Gilles Apr 1 '11 at 21:50
  • @Gilles: the file is indeed formatted DOS/Windows – Azim Apr 1 '11 at 21:54

Try this:


while IFS= read -r pp; do
  find . -iname "*${pp}*" -print0
done < /path/to/pattern.txt

Not sure why you want -print0, but I left it in anyway. Perhaps you are attempting to pipe this to xargs?

  • Thanks for the suggestion but the find command still doesn't find anything. – Azim Apr 1 '11 at 21:45
  • Yes at the end I will pipe the results to xargs to copy the files it finds. – Azim Apr 1 '11 at 21:46
  • thanks for the comment along with @Gilles observation I got it to work. – Azim Apr 1 '11 at 21:58

Here's a safe and Windows-proof way of looking for a bunch of file name patterns retrieved from a file. The approach I've chosen is to process the list of patterns into a find expression.

find_expression=$(<pattern \
    sed -e 's/^/-o\n-iname\n*/' \
        -e 's/\r\?$/*/' |            # turn each pattern into -o -iname *foo*
    tail -n +2)                      # remove spurious initial -o
set -f                               # turn off globbing
'                                    # split only at newlines
find . \( $find_expression \) -print0
set +f; unset IFS                    # restore defaults
  • @Gilles... Some people like crossword puzzles. I like trying to work out your scripts :) I think I'm starting to recognize your pattern :) ... you are allowing a single invocation of find to handle the umpteen (maybe thousands) of args, versus calling find unpteen times... The IFS pulls the multi-line output back to a string of args in the form: -iname *abc*, each seperated by -o (which I assume means "or")... Howerver I am a bit puzzled by \( \).. are they a feature of the shell to group the args, or are they a specific requirement of find?...(the puzzle is almost solved.. :) – Peter.O Apr 2 '11 at 10:10
  • @fred.bear: \( and \) are passed to find as ( and ) respectively; the only shell feature used there is the backslash to quote the next character (so that the parentheses are not interpreted as such by the shell). The rest is find expression syntax: ( and ) for expression grouping, -o as the binary “or” operator. – Gilles Apr 2 '11 at 11:17

The answer @SiegeX presents will work, but if you have a lot of files in your pattern file, this might become slow and clumsy. You might get much better performance by using a different tool, like this:

find . | grep -f pattern.txt

Yup, that was the whole thing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.