28

У меня есть список файлов, разделенных пробелами в файле list.txt . Я хотел бы скопировать их в новую папку.

Я пытался сделать:

cp `cat list.txt` new_folder

но это не сработало.

Как бы вы это сделали?

Обновить:

Спасибо за все ваши очень интересные ответы. Я не так много просил :)

После второй попытки решения выше, кажется, что cp напечатал справку по использованию, потому что папка new_folder еще не существовала! Извините, это кажется глупой ошибкой ... Когда я создаю папку раньше, она работает.

Тем не менее, я многому научился из всех ваших ответов и решений, спасибо за то, что вы потратили время на их написание.

3 ответа3

44

cat вообще не нужна:

xargs -a list.txt cp -t new_folder

Или используя длинные опции:

xargs --arg-file=list.txt cp --target-directory=new_folder

Вот некоторые версии оболочки:

Bash:

for file in $(<list.txt); do cp "$file" new_folder; done

или же

cp $(<list.txt) new_folder

ш (или Баш):

# still no cat!
while read -r line; do for file in $line; do cp "$file" new_folder; done; done < list.txt

или же

# none here either
while read -r line; do cp $line new_folder; done < list.txt

или же

# meow
for file in $(cat list.txt); do cp "$file" new_folder; done

или же

# meow
cp $(cat list.txt) new_folder
31

Попробуйте использовать xargs:

cat list.txt | xargs -J % cp % new_folder

Обновить:

Я сделал это на OS X, версия которого отличается от версии GNU/Linux. У xargs который приходит из GNU findutils , нет -J но есть -I что похоже (как отметил Деннис Уильямсон в комментарии). Версия xargs для OS X имеет и -I и -J которые ведут себя немного по-разному - либо подойдут для этого первоначального вопроса.

$ cat list.txt
one
two.txt
three.rtf

$ cat list.txt | xargs -J % echo cp % new_folder
cp one two.txt three.rtf new_folder

$ cat list.txt | xargs -I % echo cp % new_folder
cp one new_folder
cp two.txt new_folder
cp three.rtf new_folder
7

Раньше у меня был смущающий круговой ответ здесь, но ответ Дениса напомнил мне, что я пропустил самую основную вещь. Итак, я удалил свой оригинальный ответ. Но так как никто не сказал эту самую основную вещь, я думаю, что это стоит поместить здесь.

Исходный вопрос: «У меня есть текстовый файл со списком разделенных пробелами имен файлов. Как я могу скопировать их в один целевой каталог. »Сначала это может показаться сложным или сложным, потому что вы думаете, что вы должны каким-то образом извлечь элементы из файла определенным образом. Тем не менее, когда оболочка обрабатывает командную строку, первое, что она делает, - разделяет список аргументов на токены и (это бит, который никто не сказал прямо) пробелами отдельные токены. (Новые строки также разделяют токены, поэтому тест Дуга Харриса со списком, разделенным новой строкой, дал тот же результат.) То есть оболочка ожидает и может уже обрабатывать разделенный пробелами список.

Поэтому все, что вам нужно сделать, это поместить разделенный пробелами список (который у вас уже есть) в нужное место в вашей команде. Ваша команда является некоторым вариантом этого:

cp file1 file2 file3...file# target

Единственная проблема заключается в том, что вы хотите получить список файлов с 1 по # из вашего текстового файла.

Как отмечает Деннис в своем комментарии, ваша первоначальная попытка (cp cat list.txt new_folder) уже сработала. Зачем? Потому что внутренняя команда cat list.txt сначала обрабатывается оболочкой и раскрывается в file1 file2 file3...file# , что именно то, что оболочка ожидает и хочет в этой части команды. Если это не сработало, либо (1) у вас была опечатка, либо (2) ваши имена файлов были как-то странными (в них были пробелы или другие необычные символы).

Причина того, что все ответы Денниса работают, заключается в том, что они предоставляют необходимый список файлов для работы cp , размещая этот список в том месте, где он находится во всей команде. Опять же, сама команда имеет следующую структуру:

cp list-of-files target_directory

Легко увидеть, как все это объединяется в этой версии:

cp $(<list.txt) new_folder

$() заставляет оболочку запускать команду в круглых скобках, а затем подставлять вывод в эту точку в большей строке. Затем оболочка запускает линию в целом. Кстати, $() - более современная версия того, что вы уже делали с обратными галочками (`). Далее: < является оператором перенаправления файлов. Он говорит оболочке сбросить содержимое list.txt на стандартный ввод. Поскольку бит $() обрабатывается первым, вот что происходит поэтапно:

  1. cp $(<list.txt) new_folder # разбить строку на три токена # split line into three tokens: cp, $(<list.txt), new_folder
  2. cp file1 file2 file3...file# new_folder # substitute result of $(<list.txt) into the larger command

Очевидно, что шаг 2 - это просто обычная команда cp вы хотели.

Я понимаю, что много бью эту (возможно, очень мертвую) лошадь, но думаю, это стоит того. Понимание того , как оболочка обрабатывает команду, может помочь вам написать ее лучше и значительно упростить. Он также покажет вам, где могут скрываться проблемы. В этом случае, например, мой первый вопрос к вам должен был быть о забавных именах файлов или возможной опечатке. Никакой акробатики не было необходимости.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .