Как правильно использовать xargs с cp

Что я хочу сделать, так это прочитать список имен файлов из индексного файла, а затем скопировать каждый из перечисленных файлов в другой каталог, но у меня возникли небольшие проблемы с правильным соединением xargs и cp.

head -n 150 ../indexes/index.txt | xargs cp ~/path/to/destination/

Это дает следующую ошибку:

cp: target 'foo.txt': Not a directory

где foo.txt — это 150-е имя файла, указанное в index.txt. Очевидно, что cp должен принимать входные данные от xargs в качестве первого операнда, а не второго, но как мне заставить его это сделать?

🤔 А знаете ли вы, что...
С помощью Bash можно создавать и запускать скрипты для автоматизации повседневных задач.


30
2

Ответы:

Решено

xargs имеет параметр -I, который по сути позволяет вам указать шаблон замены и записать шаблон замены в команде (cp, в вашем случае). Затем, когда команда выполняется, все экземпляры этого шаблона замены заменяются следующей строкой ввода. Итак, ваша командная строка должна выглядеть примерно так:

head -n 150 ../indexes/index.txt | xargs -I {} cp {} ~/path/to/destination/

Без -Icp просто видит вторую строку ввода как пункт назначения, что не предназначено.

Обновлять: Итак, разбивка происходящего: Поскольку каждая строка ввода считывается из head, поскольку вы настроили канал, вывод head поступает в канал. Когда xargs получает новую строку из канала, потому что вы указали ... -I {} cp {} ..., вместо того, чтобы пытаться создать одну большую строку аргумента, состоящую из каждой строки ввода, и пытаться передать ее в cp, он выполняет cp, используя каждую строку ввода в качестве исходный операнд.

Чтобы объяснить немного дальше, рассмотрим этот сценарий. У нас есть файл с именем test.txt, который содержит следующее:

some_file.txt
another_file.txt
a_third_file.txt

Теперь предположим, что мы хотим написать команду для копирования всех этих файлов в заданное место назначения. Если просто написать...

cat test.txt | xargs cp /path/to/destination

...тогда cp эффективно видит следующее:

cp some_file.txt another_file.txt a_third_file.txt

В этой форме он пытается рассматривать a_third_file.txt как каталог и копировать в него два предыдущих файла (поэтому в вашем случае cp говорит, что 150-й путь не является каталогом). xargs -I {} исправляет это:

cat test.txt | xargs -I {} cp {} ~/path/to/destination

Теперь, вместо того, чтобы cp выполнялась только один раз, cp выполняется три раза:

cp some_file.txt ~/path/to/destination
cp another_file.txt ~/path/to/destination
cp a_third_file.txt ~/path/to/destination

При использовании версии GNU cp у него есть опция -t directory, чтобы указать место назначения для копирования всех заданных имен файлов, вместо того, чтобы это было последним аргументом:

head -n 150 ../indexes/index.txt | xargs cp -t ~/path/to/destination/