Подтвердить что ты не робот

Скопируйте структуру папок с файловой системой Unix

В оболочке 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
4b9b3361

Ответ 1

В bash:

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

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

Ответ 2

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

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

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

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

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

Ответ 3

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

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

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

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

где./from и. /to являются каталогами mv от и до.

Ответ 4

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

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

Ответ 5

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