Копирование файлов оболочки Unix, выравнивание структуры папок

StackOverflow https://stackoverflow.com/questions/27621

  •  09-06-2019
  •  | 
  •  

Вопрос

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

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

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

/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
Это было полезно?

Решение

В bash:

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top