Как удалить каталоги на основе вывода `find`?

148

Я выполняю следующую команду, чтобы найти каталоги .svn:

find . -name ".svn"

Это дает мне следующие результаты:

./toto/.svn
./toto/titi/.svn
./toto/tata/.svn

Как я могу обработать все эти строки rm -fr, чтобы удалить каталоги и их содержимое?

Arnaud
источник
3
У GNU find есть -deleteопция.
Марко
5
Или вы можете добавить -exec rm -r "{}" \;в конец находки - будьте осторожны при использовании rm -r! :)
Драв Слоан
10
@Marco Опция удаления, похоже, не работает с каталогами.
Арно
@SuperChafouin Отлично работает здесь с файлами и каталогами. Дело в том, что он удаляет только каталоги emtpy, и при его указании -name ".svn"он соответствует только .svnсамому каталогу, а не файлам, расположенным в .svnкаталоге.
Марко
1
@SuperChafouin, но не будет работать для путей с пробелами в них (следовательно, используется -execс кавычками "{}").
Драв Слоан

Ответы:

195

Find может выполнять аргументы с -execопцией для каждого найденного совпадения. Это рекомендуемый механизм, потому что вы можете правильно обрабатывать пути с пробелами / символами новой строки и другими символами. Вам придется удалить содержимое каталога, прежде чем вы сможете удалить сам каталог, поэтому используйте для -rэтого rmкоманду.

Для вашего примера вы можете выдать:

find . -name ".svn" -exec rm -r "{}" \;

Вы также можете указать find только найти каталоги с именем .svn, добавив -type dпроверку:

find . -name ".svn" -type d -exec rm -r "{}" \;

Предупреждение Используйте rm -rс осторожностью, чтобы удалить папку и все ее содержимое.

Если вы хотите удалить только пустые каталоги, а также каталоги, которые содержат только пустые каталоги, find может сделать это самостоятельно с помощью -deleteи -empty:

find . -name ".svn" -type d -empty -delete
Драв Слоан
источник
22
Я видел советы , чтобы всегда работать -type после -name того, как в командах найти, так как звонки на statполучить тип дороги. Я сам попробовал это на довольно большой пачке файлов, и это похоже на правду: работа find . -name 'foo' -type dзаняла 19 секунд, а find . -type d -name 'foo'заняла 32 секунды. Так что на 50% больше времени, чтобы бежать -typeпервым.
раскрутка
6
Я использую эту команду в течение многих лет, но на Mac теперь я получаю сообщения о том, что эти каталоги не существуют. Даже при том, что это удаляет их. Я никогда не видел сообщений раньше.
Чови
1
То же, что и @chovy. Есть ли способ избавиться от этих сообщений?
Clément
2
@chovy @ clément Это потому, что findхочет видеть в этой папке другие совпадения, одновременно удаляя папку. ~ Я пока не знаю, как это исправить. ~ Грязное исправление:find . -name "folder-to-delete" -print0 | xargs -r0 -- rm -r
Чарли
5
@chovy @ Clément Я думаю, что -depthаргумент исправляет это:find . -depth -name ".svn" -type d -exec rm -r "{}" \;
gimboland
67

Вот портативный еще быстрее, чем принятый способ ответа.

Использование +вместо точки с запятой в качестве разделителя findкоманд оптимизирует использование ЦП. Это может быть важно, если у вас много .svnподкаталогов:

find . -name .svn -type d -exec rm -rf {} +

Следует также отметить , что вы никогда не 1 необходимости цитировать фигурные скобки здесь.

1 Если вы не используете fishоболочку.

jlliagre
источник
5
Какая разница между + и точкой с запятой? Почему мы не используем фигурные скобки?
Шичен Го
1
@ShichengGuo С точкой с запятой, будет найдена одна команда rm для каждого найденного каталога, а с помощью + команда + rm обработает все найденные каталоги (или, по крайней мере, очень большое их количество). Ваш второй вопрос не получится, вьющийся здесь используются скобки.
jlliagre
Я думаю, что @ShichengGuo означает, почему нам не нужно указывать здесь фигурные скобки (@jlliagre написал, что нам никогда не нужно их заключать в кавычки). Я не могу найти ссылку сейчас, но я понимаю, что это потому, что find автоматически экранирует пути, замененные на {}.
Куинн Комендант
Ответ и вопрос не совсем верны в отношении того, что делает +. Если найдено много файлов, то ';' выдаст ошибку «слишком длинная командная строка». + разбивает файлы, найденные в пакетах, которые меньше максимально допустимой длины командной строки, и запускает команду для каждого пакета.
gaoithe
1
@gaoithe Я сделал откат, который вы редактировали, который заменил правильное утверждение неправильным. Использование + же снизить нагрузку на процессор, используя ; не приводит к команде слишком долго ошибка.
Jlliagre
27

Предположим, вы используете gnu find , вы можете использовать -deleteопцию:

find . -name test -delete

что легче запомнить.

Чун ян
источник
Подумайте о расширении вашего поста объяснением команды (или документацией для резервного копирования вашего решения). Часто одна (или две) строковые ответы не являются наиболее яркими.
HalosGhost
94
Это не работает на непустых каталогах.
belacqua
2
также работает на Mac OS X
отрисовка
Это не работает для непустой папки, но это более простое и безопасное решение
RousseauAlexandre
Порядок опций очень важен, команда find выполнит их по порядку, так что, - удаление должно быть последним
Jose Ignacio Centeno
13

На моем компьютере, когда я использую:

find . \( -name dirname -type d \) -exec rm -r '{}' ';'

Каталоги удалены, но я получаю ошибку:

find: ‘./dirname’: No such file or directory

для каждого каталога.

Мои каталоги не пусты, поэтому опция -delete для меня не сработает. Я нашел причину такого поведения здесь :

  1. find берет (не обязательно) первую запись в каталоге ./. это будет, например, dir.1 /
  2. он сравнивает его с шаблоном «dir.?». это совпадает? да.
  3. find выполняет "rm -r dir.1".
  4. find пытается ввести dir.1 /, чтобы найти шаблон в каталоге. он ничего не знает о команде exec.
  5. он не находит dir.1 / больше. возвращает ENOENT (посмотрите на вывод strace)

Я использовал это вместо этого, чтобы обойти:

rm -r `find . -name dirname -type d`

Имейте в виду, что find по-прежнему будет пытаться перейти в каталоги с именем dirname, что на самом деле не является необходимым и займет некоторое дополнительное время. В зависимости от структуры вашего каталога, вы можете обойти это с помощью --depthопции поиска. Кроме того, если у вас есть структура каталогов, такая как dirname / foo / dirname, вы получите сообщение об ошибке «Нет такого файла или каталога» от rm. Чтобы подавить ошибки, вы можете перенаправить stderr в / dev / null или использовать -fфлаг (force) с rm.

Самуил
источник
2
Плохая идея: имя файла с пробелами вызовет всевозможные ужасные проблемы
Clément
2
Для будущих читателей: find . -name "to-delete" -print0 | xargs -r0 -- rm -rэто отказоустойчивая версия, которая не падает на пробелах
Чарли
3
См. Unix.stackexchange.com/a/115869/8257, который необходимо добавить -prune.
Мапио
1
Добавьте, -pruneчтобы избежать ошибки «Нет такого файла или каталога».
Жюльен Карсик
12

Более быстрый способ сделать это:

find . -name ".svn" -type d -prune -exec rm -rf '{}' '+'

Если у вас есть «.svn» внутри другого «.svn».

Чанг Ю-Хенг
источник
Это не очень полезно, если вы хотите найти каталоги для удаления с подкаталогами.
Алексис Уилке
5

Bash конкретное решение:

shopt -s globstar
rm -r **/.svn
shopt -u globstar #optional. this will disable globstar expansion
порыв
источник
3
Примечание: для расширения командной строки глобусов, которые соответствуют многим файлам, существует ограничение на количество файлов, которые вы можете сопоставить с этим механизмом. Превышение этого предела приведет кbash: /bin/rm: Argument list too long
Драв Слоан
1
@DravSloan верен, но это ограничение в сотнях тысяч файлов. Об этом нужно помнить, но, вероятно, это не будет проблемой для большинства людей.
evilsoup
4

Я обнаружил, что -deleteдействие хорошо работает с -pathтестом. Например, следующее должно работать на оригинальной проблеме плакатов:

find . -path '*/.svn*' -delete
Магнус
источник
Ты уверен? -deleteподразумевает -depth, и это, безусловно, удаляет непустые каталоги в моей системе.
Магнус
Проверено, и это работает. Может быть, это была просто опечатка, которую я исправил.
kenorb
Я также попробовал этот подход, и он работал - каталоги были удалены.
Аллан