Shivpe_R
Shivpe_R

Reputation: 1080

Find files with names matching one of many patterns and copy to different directory

I have a set of files in a Linux directory. I want to copy the files whose names match one of many patterns to a different directory; I'm placing all those pattern in a text file.

What is a possible way I could find all the files with the pattern and copy to a destination folder?

Until now, I was able to do just keep the patterns like this:

find . -type f \( -name "*Patt1* -o -name "*Patt2*" - o -name "*Patt3*" \
    -o -name "*Patt4*" -o -name "*Patt5* -o -name "*Patt6*" \) \
    | xargs cp {} /home/DestinationFolder/.

My pattern file looks like this:

PYTH
SPYD
ISIN
CUSIP
HELD
SEDO

Upvotes: 0

Views: 5526

Answers (2)

Benjamin W.
Benjamin W.

Reputation: 52361

You can filter your filenames with grep, using the -f option to read the patterns from a file:

find . -type f -print0 | grep -zFf pattern.txt \
    | xargs -0 -I {} cp {} /home/DestinationFolder

Since your patterns, according to your comment, are just fixed strings, we can add -F to the grep options to interpret the lines from the input file as fixed strings.

To accommodate any possible character in filenames, I use -print0 to print the filenames null byte separated; grep -z reads to and writes from a null byte separated stream; and xargs -0 expects null byte separated input.

Upvotes: 1

cschuyle
cschuyle

Reputation: 1

I am on MacOS. My problem was very similar you yours, but I wanted to use a regex to match the files to be operated on. The first answer pointed me in the right direction, but sadly (or not?) I am on MacOS. Evidently, BSD find doesn't like -printf, and BSD grep doesn't like -z. So, fiddlesticks.

On MacOS (10.13: High Sierra), and using bash (I tested the MacOS-supplied one and GNU bash 4.4.19), the following does what you need. As a bonus, it's resilient with respect to filenames that contain spaces (but not all weird filenames), and allows patterns to be extended regexs instead of only simple fixed strings. The example below includes an extended regex pattern (the first line in the file) which I actually used to do a similar operation: moving files that end in patterns like _9.mpg to a different directory:

Caveat: To make spaces play nice for files with spaces in them (and who doesn't have spaces in their filenames, eh??), I'm using this hack, which will cause bash to split pipes between commands on newlines, instead of spaces:

 OLD_IFS=$IFS
 IFS=$(echo -en "\n\b")

Here is a test "source" directory:

% ls -1 source
bad dog_1.mp3
bod dog.mp3
i am SEDO yeah

Here is the empty "target" directory:

% ls ../target|wc -l
       0

You can make a pattern file containing whatever patterns are legal extended regexs, which includes simple strings which don't contain special regex characters. If you revile regexs and want just simple strings (and are therefore more sane than I), change 'grep -Ef' above to 'grep -f':

% cat patterns.txt
_\d+\.m..$
PYTH
SPYD
ISIN
CUSIP
HELD
SEDO

Here is the file with the meat:

% cat generate-commands.sh
for f in $(find ${src} -type f | grep -Ef ${pats}); do
  echo mv \"$f\" \"${trg}/$f\"
done

And here we go! First, cd into the source dir, then run the generate-commands file with the required params:

% cd source
% src=. trg=../target pats=../patterns.txt . ../generate-commands.sh
mv "./i am SEDO yeah" "../target/./i am SEDO yeah"
mv "./bad dog_1.m4v" "../target/./bad dog_1.m4v"

Then, ever so carefully, thoughtfully consider actually executing the commands that were output by the generate-commands.sh script.

Finally, unless you like surprises, it's probably a good idea to either either kill your current shell, or do this:

IFS=$OLD_IFS

Upvotes: 0

Related Questions