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?