Search for multiple patterns from a file using find command [duplicate]
There is an option to use grep -f MY_FILE
to let it search for patterns taken from a file, instead of being specified directly on the command line.
Is there an option to do something similar with the find
command and let it read the patterns to search from an input file?
Solution 1:
find
doesn't seem to have such capabilities built in, but you can use xargs
to construct multiple find
commands using arguments from a file, like:
xargs -a patterns.txt -I% find Pictures/ -name %
where patterns.txt
would be a list of patterns suitable for the -name
filter, one pattern per line. Pay attention that you don't have leading/trailing spaces in there, as they would be included in the pattern. An example:
*.jpg
2018-06-*
*foo*
unicorn.png
Note: While this answer looks quite easy and elegant, it got correctly pointed out in the comments that it has a few downsides:
Performance is not too great for large folders or many patterns, as it will run
find
once per pattern in your file, causing it to repeatedly scan the whole search folder.Because of that, also if you have multiple patterns which could potentially overlap (like
*.jpg
and*foo*
), files matching more than one pattern will appear that many times in the result. If you're only printing the names anyway, you could pipe the output throughsort -u
to remove duplicates, but if you e.g. remove those results or run any-exec
commands on them, this may be more undesirable.If any of these downsides are a problem for your use case, maybe better go for one of the alternative answers.
Explanation of the command:
-
xargs
will read a list of arguments and use them to construct and run a new command line. -
-a patterns.txt
tells it to read from that file instead of the standard input. -
-I%
tells it to not simply append the arguments it read to the end of the command line, but to replace the%
character in the command line you supplied with one argument. This implies creating and running one separate command per input argument. -
find Pictures/ -name %
is the command line into which we want to insert the arguments, replacing the%
. You don't need quoting here, becausexargs
will take care that each argument it inserts will be treated as single token and not get split on its own. You can of course replace thePictures/
with your own search directory and also use a different and/or more filters other than just-name
. Because we use the insert option, you can also append actions like-exec ...
to the end of the command.
Solution 2:
You can simply create a regex from the contents of your file using paste -sd'|' bar
.
find ~/foo -regextype egrep -regex "^.*/($(paste -sd'|' bar))$"
The regex will be "^.*/(a|b)$"
Solution 3:
Not too recently, I've made an answer that combines multiple patterns using -regex
flag in find
. Based on that, we can make a small script or function to do that same job, but build up the list of patterns from file.
#!/bin/bash
read_file(){
local full_pattern=""
while IFS= read -r pattern; do
if [ -z "$full_pattern" ];then
full_pattern="$pattern"
continue
fi
full_pattern="$full_pattern\|$pattern"
done < "$1"
echo "$full_pattern"
}
fp=$(read_file "$1" )
find "$2" -type f -regex ".*\($fp\).*$"
What this does:
- we call script as
findf.sh input.txt /etc
, where first positional parameter is the file with patters, and second - directory where to search. GNU find assumes.
directory if directory argument is omitted , so$2
isn't erequired. - The function
read_file
reads the input file that is first positional parameter to script. This builds up a pattern for-regex
flag. - This pattern is echoed back to "main" block of the script, and saved to
fp
variable, and that gets passed into thefind
command.