Running find
once per filename, and assuming that no filename contain embedded newlines:
#!/bin/sh
mkdir -p destination_folder || exit 1
while IFS= read -r name; do
find . -path ./destination_folder -prune -o \
-type f -name "$name" -exec cp {} destination_folder \;
done <spreadsheet.txt
This first creates the destination directory in the current directory (and terminates if that fails). It then reads the input file, line by line, and calls find
to locate any regular file that has that name. The destination directory is explicitly avoided by find
using -prune
whenever we come across that in our search.
Whenever a file with the correct name is found, it is copied to the destination directory. If multiple files has the same name, the copy in in destination_folder
will be overwritten.
If the current directory is huge, or if the list of filenames is long (but not many thousands of lines), then this will be a slow operation. We may therefore choose to do a single call to find
. The following code assumes that it's executed with e.g. bash
as it uses arrays:
#!/bin/bash
mkdir -p destination_folder || exit 1
names=()
while IFS= read -r name; do
names+=( -o -name "$name" )
done <spreadsheet.txt
find . -path ./destination_folder -prune -o \
\( "${names[@]:1}" \) -type f -exec cp -t destination_folder {} +
Here, I've also chosen to use cp -t
(a GNU cp
extension) to be able to call cp
as few times as possible and not once per found file.
The code above builds an array, names
, that eventually will be on the correct format for use with find
. The command that is actually executed at the end of the above code, given the example in your question, will be
find . -path ./destination_folder -prune -o '(' -name AB.txt -o -name CD.txt -o -name EF.txt ')' -type f -exec cp -t destination_folder '{}' +
To avoid the issue of filename collisions in the destination directory, if you're using GNU cp
(e.g. on a Linux system), is to use cp
with its -b
or --backup
option.
On non-Linux systems, GNU cp
may often be available as gcp
after installing GNU coreutils via a package manager.
That last script, but for /bin/sh
(no arrays):
#!/bin/sh
mkdir -p destination_folder || exit 1
set --
while IFS= read -r name; do
set -- -o -name "$name" "$@"
done <spreadsheet.txt
shift
find . -path ./destination_folder -prune -o \
\( "$@" \) -type f -exec cp -t destination_folder {} +