Сведения о вопросе

PIRLO

21:07, 3rd August, 2020

Теги

unix   shell    

Unix shell копирование файлов выравнивание структуры папок

Просмотров: 390   Ответов: 5

На UNIX bash shell (в частности, Mac OS X Leopard) какой самый простой способ скопировать каждый файл, имеющий определенное расширение, из иерархии папок (включая подкаталоги) в одну и ту же папку назначения (без вложенных папок)?

Очевидно, что существует проблема наличия дубликатов в исходной иерархии. Я бы не возражал, если бы они были переписаны.

Пример: мне нужно скопировать каждый файл .txt в следующей иерархии

/foo/a.txt
/foo/x.jpg
/foo/bar/a.txt
/foo/bar/c.jpg
/foo/bar/b.txt

В папку с именем 'dest' и получить:

/dest/a.txt
/dest/b.txt



  Сведения об ответе

park

17:05, 2nd August, 2020

В bash:

find /foo -iname '*.txt' -exec cp \{\} /dest/ \;

find найдет все файлы по пути /foo , соответствующие подстановочному знаку *.txt, без учета регистра (именно это и означает -iname ). Для каждого файла find выполнит cp {} /dest/, а найденный файл заменит {} .


  Сведения об ответе

9090

09:03, 3rd August, 2020

Единственная проблема с решением Магнуса заключается в том, что он разветвляет новый процесс "cp" для каждого файла, что не очень эффективно, особенно если существует большое количество файлов.

На Linux (или других системах с GNU coreutils) вы можете сделать:

find . -name "*.xml" -print0 | xargs -0 echo cp -t a

(Значение -0 позволяет ему работать, когда ваши имена файлов имеют странные символы - например, пробелы-в них.)

К сожалению, я думаю, что Mac поставляются с инструментами в стиле BSD. Кто-нибудь знает "standard", эквивалентный переключателю "- t"?


  Сведения об ответе

PHPH

21:28, 28th August, 2020

Ответы выше не допускают коллизий имен, так как Аскер не возражал против перезаписи файлов.

Я действительно против того, чтобы файлы переписывались, поэтому придумал другой подход. Заменив каждый / в пути на - Сохранить иерархию в именах и поместить все файлы в одну плоскую папку.

Мы используем find для получения списка всех файлов, затем awk для создания команды mv с исходным именем файла и измененным именем файла, а затем передаем их в bash для выполнения.

find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash

где./от и./являются каталоги в МВ от и до.


  Сведения об ответе

ASER

14:56, 16th August, 2020

Если вы действительно хотите запустить только одну команду,почему бы не создать одну и не запустить ее? Вот так:

$ find /foo  -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx

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

cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'

Я думаю, что самый чистый-это:

$ find /foo  -name '*.txt' | xargs -i cp {} /dest

Меньше синтаксиса для запоминания, чем параметр-exec.


  Сведения об ответе

ITSME

22:02, 13th August, 2020

Что касается справочной страницы для cp на коробке FreeBSD, то здесь нет необходимости в переключателе a-T. cp будет считать, что последний аргумент в командной строке является целевым каталогом, если передается более двух имен.


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться