Copying files in a pipeline using cygwin bash shellscript

2

I need to select certain files in a directory and copy them to another directory using a cygwin script but I cannot get the syntax for the copy. i.e.

for i in cat boards do ls -t $work |grep ^$i |cp .............

If I run with just the grep it lists all the files I want but I don't know how to get that on the cp cmnd.

Long time since I did any of this but seem to remember there is an automatic variable containing the input from stdin. Any help appreciated.

Stumac

Posted 2017-09-22T17:00:18.253

Reputation: 21

Answers

1

Sounds like you're hunting for xargs.

destination="/path/to/folder"
for i in $(cat boards)
do 
     ls -t $work | grep ^$i | xargs -Isource cp source /path/to/destination
done

Run a for loop using variable i filled with the results of cat boards ($() spawns a process).

xargs -Isource takes the output of grep ^$i and feeds it to cp in the source position of cp source /path/to/destination. From the man page:

-I replace-str Replace occurrences of replace-str in the initial-arguments with names read from standard input.

Hope this was what you were looking for.

Peter Berbec

Posted 2017-09-22T17:00:18.253

Reputation: 158

1

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).

minnmass

Posted 2017-09-22T17:00:18.253

Reputation: 111

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.527

1The {} 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 a cannot stat with the filename as if it started with a quote. – Peter Berbec – 2017-09-23T00:04:48.777

Deep; 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