Я хочу рекурсивно удалить все файлы, к которым не было доступа в данный момент, в папке a
, кроме всех файлов в подпапке b
.
find a \( -name b -prune \) -o -type f -delete
Тем не менее, я получаю сообщение об ошибке:
find: действие -delete автоматически включает -depth, но -prune ничего не делает, когда действует -depth. Если вы хотите продолжить, просто явно используйте опцию -depth.
Добавление -depth
приводит b
к включению всех файлов , что не должно происходить.
Кто-нибудь знает безопасный способ сделать эту работу?
a
кромеa/b
?cd a && ls -d !(b/*)
сработает? (Чтобы сделать это, простоrm -r
вместоls -d
.)a
(кроме файлов вa/b
).-r
до рм. Кажется, что на вопрос, о котором вы спрашиваете, довольно легко ответить, используя расширенное глобирование в bash, и тогда то, что вы будете делать с результатом глобализации, зависит от вас.Ответы:
TL; DR: лучший способ - использовать
-exec rm
вместо-delete
.Объяснение:
Почему находите жалобу, когда вы пытаетесь использовать
-delete
с-prune
?Краткий ответ: потому что
-delete
подразумевает-depth
и-depth
делает-prune
неэффективным.Прежде чем мы перейдем к длинному ответу, сначала рассмотрим поведение поиска с и без
-depth
:Там нет гарантии о заказе в одном каталоге. Но есть гарантия, что каталог обрабатывается до его содержимого. Примечание
foo/
перед любымfoo/*
иfoo/bar
перед любымfoo/bar/*
.Это может быть отменено с
-depth
.Обратите внимание, что теперь все
foo/*
появляются раньшеfoo/
. То же самое сfoo/bar
.Более длинный ответ:
-prune
предотвращает поиск в каталоге. Другими словами-prune
пропускает содержимое каталога. В вашем случае не-name b -prune
позволяет найти спуск в любой каталог с именемb
.-depth
делает find для обработки содержимого каталога до самого каталога. Это означает, что к тому времени, когда find начнет обрабатывать запись каталога,b
ее содержимое уже обработано. Таким образом-prune
, неэффективно с-depth
по сути.-delete
подразумевает,-depth
что он может сначала удалить файлы, а затем пустой каталог.-delete
отказывается удалять непустые каталоги. Я думаю, что можно было бы добавить опцию, чтобы заставить-delete
удалить непустые каталоги и / или запретить-delete
подразумевать-depth
. Но это уже другая история.Есть еще один способ добиться того, чего вы хотите:
Это может или не может быть легче запомнить.
Эта команда все еще спускается в каталог
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
) и проверить вывод.источник
-not -path
была только вещь! Спасибо за щедрое объяснение!-not -path
работает, а-prune
не будет полезно. Почему может-not -path
сосуществовать с-depth
?Просто используйте
rm
вместо-delete
:источник
rm
работает, аdelete
что нет?rm
не имеет этой проблемы. Но, независимо от этого, разработка была бы хорошей вещью.-delete
подразумевает-depth
, что, очевидно, не может работать с-prune
.-path
работает, но не останавливаетсяfind
от спуска в каталогах, которые не нужно исследовать.Приведенные выше ответы и объяснения были очень полезны.
Я использую обходные пути "-exec rm {} +" или "-not -path ... -delete", но они могут быть намного медленнее, чем "find ... -delete". Я видел "find ..." -delete "работать в 5 раз быстрее, чем" -exec rm {} + "в глубоких каталогах в файловой системе NFS.
Решение «-не путь» имеет очевидные накладные расходы при просмотре всех файлов в исключенных каталогах и ниже.
"Find .. -exec rm {} +" вызывает rm, который выполняет системные вызовы:
«Find -delete» выполняет системные вызовы:
Таким образом, команда "-exec rm {} +" rm дважды выполняет полный путь к поиску inode для каждого файла, а команда "find -delete" выполняет статистику и отмену ссылки на имя файла в текущем каталоге. Это большая победа, когда вы удаляете много файлов в одном каталоге.
(режим нытья включен (извините))
Похоже, что дизайн взаимодействия между -depth, -delete и -prune без необходимости устраняет наиболее эффективный способ выполнения общего действия «удалять файлы, кроме тех, что находятся в каталогах -prune»
Комбинация "-type f -delete" должна запускаться без -depth, поскольку она не пытается удалить каталоги. В качестве альтернативы, если бы «find» имела действие «-deletefile», которое говорит, что не удаляет каталоги, -depth не должно подразумеваться.
Вызовы xargs или find -exec для команды rm могут быть ускорены, если у rm есть возможность сортировать имена файлов, открывать каталоги и выполнять unlinkat (dir_fd, filename) вместо отмены связывания полных путей. Он уже выполняет unlinkat (dir_fd, имя файла) при рекурсии через каталоги с опцией -r.
(режим нытья выключен)
источник