What I want to do is read a list of filenames from an index file and then copy each listed file to another directory, but I'm having a little trouble properly stringing xargs and cp together.
head -n 150 ../indexes/index.txt | xargs cp ~/path/to/destination/
This gives the following error:
cp: target 'foo.txt': Not a directory
where foo.txt
is the 150th filename listed in index.txt. Clearly cp should be taking the input from xargs as its first operand and not the second, but how do I get it to do this?
CodePudding user response:
xargs
has a -I
parameter, which essentially allows you to specify a substitution pattern and write a substitution pattern in a command (cp
, in your case). Then, when the command is executed, all instances of that substitution pattern are replaced with the next line of input. So, your command string should look something like this:
head -n 150 ../indexes/index.txt | xargs -I {} cp {} ~/path/to/destination/
Without the -I
, cp
just sees the second line of input as the destination, which is not what's intended.
Update: So, a breakdown of what's happening:
As each line of input is read from head
, since you've set up a pipe, the output of head
goes to the pipe. When xargs
gets a new line from the pipe, because you've specified ... -I {} cp {} ...
, instead of trying to create one large argument string composed of each line of input and trying to pass that to cp
, it executes cp
using each line of input as the source operand.
To explain a little further, consider this scenario. We have a file called test.txt
that contains the following:
some_file.txt
another_file.txt
a_third_file.txt
Now, suppose we want to write a command to copy all of those files to a given destination. If we simply write...
cat test.txt | xargs cp /path/to/destination
...then what cp
effectively sees is this:
cp some_file.txt another_file.txt a_third_file.txt
In this form, it tries to treat a_third_file.txt
as a directory and copy the previous two files to it (which is why in your case cp
says the 150th path is not a directory). xargs -I {}
fixes that:
cat test.txt | xargs -I {} cp {} ~/path/to/destination
Now, instead of cp
only being executed once, cp
gets executed three times:
cp some_file.txt ~/path/to/destination
cp another_file.txt ~/path/to/destination
cp a_third_file.txt ~/path/to/destination
CodePudding user response:
If using the GNU version of cp
, it has a -t directory
option to specify the destination to copy all the filenames given to, instead of having that be the last argument:
head -n 150 ../indexes/index.txt | xargs cp -t ~/path/to/destination/