Скопировать список файлов

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

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

cp `cat list.txt` new_folder

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

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

Обновить:

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

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

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

4 ответа

Решение

Попробуйте использовать 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

Нет необходимости cat совсем:

xargs -a list.txt cp -t new_folder

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

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

Вот несколько версий оболочки. Обратите внимание, что некоторые переменные не заключены в кавычки или for Циклы используются специально, потому что OP указал, что имена файлов разделены пробелом. Некоторые из этих методов не будут работать для ввода имени файла на строку, в котором имена файлов содержат пробелы.

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

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

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

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

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 команда, которую вы хотели.

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

В дополнение к ответу @dennis-williamson:

Если у вас есть файл с комментариями и вы хотите использовать xargs, исключите его строками, начинающимися с#используя grep:

      TARGET_PATH="/full/path/"
grep -e '^[^#]' filelist.txt | xargs \
    cp --parents \
    --target-directory "$TARGET_PATH"
Другие вопросы по тегам