I'm assuming you want all of the copied files to wind up in the same place.
It sounds you want all of the files in /path/to/$work where the filename starts with a line from the boards file to be copied to some destination.
eg., given
$ cat boards
start1
start2
$ ls $work
start1.txt
start1.jpg
start2.txt
start2.jpg
start3.txt
$
you want start1.txt, start1.jpg, start2.txt, and start2.jpg to be copied to somewhere, but not start3.txt.
Don't use xargs: it's a fantastic tool, but it's going to cause problems if any of your files or "search patterns" have spaces or glob characters (eg., "*").
Rather, use a tool that expects a search pattern, find.
cat boards | while read pattern
find $work -name "${pattern}*" -exec cp {} /path/to/destination +
done
That will read in each line of boards
as a search pattern. find
will then look in the $work
directory to find files (and folders!) that match ${pattern}*
; each found item (file or folder) will be passed to cp as a properly-escaped parameter (in the {}
spot), and it will do xargs
-like minimization of the number of cp
processes it starts because of the +
(if you wanted exactly one process per found item, you'd use a ;
instead - be sure to escape it properly, though).
find
has a rich set of filters you can apply to narrow down your search. Of particular note is the -type f
option which will limit the results to file.
Note that the cp
command may fail if there are folders in the argument list passed in from find
; using the -type f
filter will prevent that.
Note also that the cp
command will "flatten" the output directory - if $work
has subdirectories which happen to have matching files, those files will be copied straight to /path/to/destination
. If you want to only copy files at the top level of $work
, the -maxdepth
filter (probably with a value of 1: -maxdepth 1
) will prevent find
from recursing too deep down the file system.
Note also that find
is case-sensitive. If you don't want it to be, you can use -iname
instead of -name
to do a case-insensitive search.
If there are any other command line parameters you want to give cp
, just put them in front of the {}
(eg., cp -r {}
to copy directories that match the search pattern).
Good point about strange file names. There is a solution both ways, but IFS= and -print0 get ugly. A potential tweak to your find solution, maybe use
-exec cp "{}"
to catch the inevitable spaces? – Peter Berbec – 2017-09-22T23:30:49.5271The {} handles that already; quoting them will just send a literal pair of curly braces to cp, which will complain about no such file. – minnmass – 2017-09-22T23:33:39.997
1Just tested, and we're both right! :)
The bare braces works fine even with filenames including a space, as does double quoted braces (
"{}"
).The way I thought would not work, using single quoted braces (
'{}'
) worked fine as well. I assumed the the single quotes would have passed the braces as arguments, but I guess not.Escaping the braces succeeds.Escaping any of quotes fails. Escaping the braces inside any type of quotes fails with
cannot stat "\{\}"
. Escaping quotes with escaped braces fails with acannot stat
with the filename as if it started with a quote. – Peter Berbec – 2017-09-23T00:04:48.777Deep; the curlies are arguments to find, so of course quoting them would work. Sorry, I had a dumb there. – minnmass – 2017-09-23T18:56:29.533
Nope, my dumb. I was unaware the curlies escaped properly. – Peter Berbec – 2017-09-25T11:43:23.053