Скопировать список файлов
У меня есть список файлов, разделенных пробелами в файле 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
на стандартный ввод. Поскольку $()
бит обрабатывается первым, вот что происходит поэтапно:
cp $(<list.txt) new_folder
# split line into three tokens: cp, $(<list.txt), new_folder
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"