Unix shell файл копирование структуры папок - PullRequest
38 голосов
/ 26 августа 2008

В оболочке UNIX bash (в частности, 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

Ответы [ 5 ]

52 голосов
/ 26 августа 2008

В Баш:

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

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

13 голосов
/ 26 августа 2008

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

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

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

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

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

11 голосов
/ 12 июля 2010

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

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

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

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

, где ./from и ./to - это каталоги mv от и до.

3 голосов
/ 12 сентября 2008

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

$ 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.

1 голос
/ 12 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...