Unfortunately the find
command's -name
predicate only accepts a single pattern. If you want to search for multiple files by name you'd need to chain them with the -o
(logical OR
) operator - something like:
find Documents/ \( -name "file2.txt" -o -name "file5.txt" -o -name "file9.txt" \) -print
This makes it tricky to construct the search programmatically from a list; the closest I can come to your attempted command is:
read the list into a shell array
mapfile -t files < list.txt
use the bash shell's printf
to construct the predicate list
printf -- '-name "%s" -o ' "${files[@]}"
use eval
to evaluate the resulting command string
There's a wrinkle here inasmuch as if we use printf
's format re-use feature to construct the list in this way, we're left with a 'dangling' -o
; we can work around this by terminating the list with a -false
test (since -o -false
is a Boolean no-op) so that our final predicate string becomes
"\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)"
Putting it all together - given
$ tree dir
dir
├── Folder_1
│ ├── file1.txt
│ ├── file2.txt
│ ├── file3.txt
│ └── file4.txt
├── Folder_2
│ ├── file5.txt
│ ├── file6.txt
│ └── file7.txt
└── Folder_3
├── file8.txt
└── file9.txt
3 directories, 9 files
and
$ cat list.txt
file2.txt
file5.txt
file9.txt
then
$ mapfile -t files < list.txt
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -print
dir/Folder_1/file2.txt
dir/Folder_2/file5.txt
dir/Folder_3/file9.txt
To copy files instead of just listing them, you could then do
$ mkdir newdir
$ eval find dir/ "\( $(printf -- '-name "%s" -o ' "${files[@]}") -false \)" -exec cp -t newdir/ -- {} +
resulting in
$ tree newdir
newdir
├── file2.txt
├── file5.txt
└── file9.txt
0 directories, 3 files
Note: the eval
command is powerful and potentially open to abuse: use with care.
In practice, given that you appear to want to find only a small number of files, the KISS approach would be to accept the performance hit of multiple find
calls and just use a loop:
while read -r f; do
find dir/ -name "$f" -exec cp -v -- {} newdir/ \;
done < list.txt
or even using xargs
xargs -a list.txt -n1 -I@ find dir/ -name @ -exec cp -v -- {} newdir/ \;
Best Answer
Try this:
However, note that the above will not delete files whose name ends in a
.
, for examplefoo.
. To delete those as well, use this instead: