TL; DR: лучший способ - использовать -exec rm
вместо -delete
.
find a \( -name b -prune \) -o -type f -exec rm {} +
Объяснение:
Почему находите жалобу, когда вы пытаетесь использовать -delete
с -prune
?
Краткий ответ: потому что -delete
подразумевает -depth
и -depth
делает -prune
неэффективным.
Прежде чем мы перейдем к длинному ответу, сначала рассмотрим поведение поиска с и без -depth
:
$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
Там нет гарантии о заказе в одном каталоге. Но есть гарантия, что каталог обрабатывается до его содержимого. Примечание foo/
перед любым foo/*
и foo/bar
перед любым foo/bar/*
.
Это может быть отменено с -depth
.
$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
Обратите внимание, что теперь все foo/*
появляются раньше foo/
. То же самое с foo/bar
.
Более длинный ответ:
-prune
предотвращает поиск в каталоге. Другими словами -prune
пропускает содержимое каталога. В вашем случае не -name b -prune
позволяет найти спуск в любой каталог с именем b
.
-depth
делает find для обработки содержимого каталога до самого каталога. Это означает, что к тому времени, когда find начнет обрабатывать запись каталога, b
ее содержимое уже обработано. Таким образом -prune
, неэффективно с -depth
по сути.
-delete
подразумевает, -depth
что он может сначала удалить файлы, а затем пустой каталог. -delete
отказывается удалять непустые каталоги. Я думаю, что можно было бы добавить опцию, чтобы заставить -delete
удалить непустые каталоги и / или запретить -delete
подразумевать -depth
. Но это уже другая история.
Есть еще один способ добиться того, чего вы хотите:
find a -not -path "*/b*" -type f -delete
Это может или не может быть легче запомнить.
Эта команда все еще спускается в каталог b
и обрабатывает каждый отдельный файл в нем только для того, -not
чтобы отклонить их. Это может быть проблемой производительности, если каталог b
огромен.
-path
работает иначе чем -name
. -name
совпадает только с именем (файла или каталога), а -path
с полным путем. Например соблюдайте путь /home/lesmana/foo/bar
. -name -bar
будет соответствовать, потому что имя bar
. -path "*/foo*"
будет соответствовать, потому что строка /foo
находится в пути. -path
имеет некоторые тонкости, которые вы должны понять, прежде чем использовать его. Прочитайте справочную страницу find
для более подробной информации.
Помните, что это не на 100% надежно. Есть шансы на «ложные срабатывания». То, как команда написана выше, пропускает любой файл, у которого есть родительский каталог, имя которого начинается с b
(положительного). Но он также пропустит любой файл, имя которого начинается b
независимо от позиции в дереве (ложное срабатывание). Это можно исправить написав лучшее выражение, чем "*/b*"
. Это оставлено в качестве упражнения для читателя.
Я предполагаю, что вы использовали a
и в b
качестве заполнителей и настоящие имена больше похожи allosaurus
и brachiosaurus
. Если вы поставите brachiosaurus
вместо b
этого количество ложных срабатываний, будет значительно сокращено.
По крайней мере, ложные срабатывания будут не удалены, так что это будет не так трагично. Кроме того, вы можете проверить наличие ложных срабатываний, выполнив сначала команду без -delete
(но не забудьте указать подразумеваемую -depth
) и проверить вывод.
find a -not -path "*/b*" -type f -depth
a
кромеa/b
?