Можно использовать (отрицательный)-path
тест инструмента find
. Если инструмент поддерживает -delete
, то это относительно просто:
find. ! -path./folder1 ! -path './folder1/*' ! -path./file1 ! -path./folder2/subfolder/onefile -delete
Если onefile
существует, subfolder
и folder2
будут указаны в cannot delete: Directory not empty
ошибках. Это заставит их выжить, но статус выхода из find
не будет 0
.
Если ваш find
поддерживает -empty
, вы можете проверить каталоги на предмет их отсутствия перед -delete
их. В общем, вы не хотите тестировать каталоги, отличные от -, на предмет того, что они пусты. Это усложняет команду, но позволяет вернуть 0
, если все пойдет по плану. Вот так:
find. ! -path./folder1 \
! -path './folder1/*' \
! -path./file1 \
! -path./folder2/subfolder/onefile \
\( ! -type d -o -empty \) \
-delete
find
достаточно богат, чтобы поддерживать -delete
, а -empty
, вероятно, будет поддерживать -regex
. GNU find
в моем Debian может сделать это:
find. ! -regex '\./folder1/*.*\|\./file1\|\./folder2/subfolder/onefile' -delete
Примечания:
Наличие onefile
не позволяет find
удалить subfolder
или folder2
. Вам нужно больше шаблонов (, см. ниже ), или более сложное регулярное выражение, чтобы сохранить эти каталоги независимо от того, есть ли там onefile
или нет. Вариант с более сложным регулярным выражением:
find. ! -regex '\./folder1/*.*\|\./file1\|\./folder2\(/subfolder\(/onefile\)?\)?' -delete
В качестве бонуса это улучшение устраняет проблему «не -нулевой статус выхода».
-path
использует glob -как нотация сопоставления с образцом(?
соответствует одному символу, а *
соответствует нулю или более символам ); -regex
использует регулярные выражения(.
соответствует одному символу, а .*
соответствует нулю или более символам ).
-path
и -regex
соответствуют полному пути к файлу. Чтобы создать работающие шаблоны, вам нужно знать, какой путь find
будет использоваться. Подсказки:
find
обязывает не использовать завершающую косую черту для любого пути, который не является начальной точкой. По этой причине я использовал отдельные шаблоны -path
для ./folder1
и файлы внутри (./folder1/*
). .
, то все пути будут начинаться с ./
, кроме пути к самому .
, это будет просто .
.Если начальная точка ./
, то все пути будут начинаться с ./
. В моих тестах -delete
молча обрабатывает .
без удаления и без ошибок, так что нет необходимости исключать .
. С другой стороны, он выдает ошибку ./
. Я не уверен, насколько надежно такое поведение. В случае каких-либо сомнений явно исключите .
, особенно если вам нужен значимый статус выхода.
Существует несколько типов регулярных выражений . Например. Я мог использовать -regextype posix-extended
до ! -regex
. Ниже приведена самая компактная команда, которую я придумал(хотя и не переносимая):
find. -regextype posix-extended \
! -regex './folder1(/.*)?|./file1|./folder2(/subfolder(/onefile)?)?' \
-delete
Я намеренно использовал.
(подстановочный знак )вместо\.
(буквальной точки )для начального .
. Поскольку начальная точка — .
, каждый путь обязательно будет начинаться с .
, поэтому подстановочный знак не будет соответствовать ничему, кроме .
. Используйте \.
, если вы предпочитаете формальную корректность.
Если вы хотите убедиться, что не удаляете слишком много, вы можете «пробный запуск» команды. Измените -delete
на -print
, вы увидите, что будет удалено. Это самая первая команда, измененная таким образом :
find. ! -path./folder1 ! -path './folder1/*' ! -path./file1 ! -path./folder2/subfolder/onefile -print
Или отрицать все предыдущие тесты в целом (, что не то же самое, что отрицание их по отдельности, если только вы не знаете, что еще нужно изменить; сравните это с с этим)и измените -delete
на -print
, чтобы увидеть, что выживет:
find. ! \( ! -path./folder1 ! -path './folder1/*' ! -path./file1 ! -path./folder2/subfolder/onefile \) -print
Без кавычек ./folder1/*
будет ошибкой, :оболочка попытается раскрыть *
.
-path
является обязательным для POSIX , а -delete
, -regex
и -empty
— нет. Иногда вместо -delete
можно использовать -exec rm {} +
, но такой rm
не удалит каталоги, нужен rmdir
. Да, rm -r
может удалить каталоги, но тогда rm -r./folder2
также удалит ./folder2/subfolder/onefile
и бросит вызов всему смыслу.
Тем не менее наш базовый подход можно реализовать без-delete
(и несколько улучшить):
find. -depth \
! -path . \
! -path ./folder1 \
! -path './folder1/*' \
! -path ./file1 \
! -path ./folder2 \
! -path ./folder2/subfolder \
! -path ./folder2/subfolder/onefile \
\( \( ! -type d -exec rm {} + \) -o \( -type d -exec rmdir {} + \) \)
Явным образом исключив ./folder2
и ./folder2/subfolder
, я заставил команду возвращать осмысленный статус выхода и сохранять эти каталоги, даже если onefile
не существует.
Если вы хотите удалить каталоги, если onefile
не существует, не используйте дополнительные шаблоны. Исследуйте вариант --ignore-fail-on-non-empty
для rmdir
. Эта опция не требуется для POSIX.
Не скажу, что эта последняя команда компактна. Мне больше нравится вариант с -regex
.
Для такой потенциально разрушительной операции я бы сделал это осторожно, вручную :получив список всех файлов с полными путями; отредактируйте файлы, которые должны остаться (grep -v
, или с помощью вашего любимого текстового редактора ); проверить, чтобы ничего не проскользнуло; дайте списокrm
(возможно, потребуется использовать xargs(1)
, если список огромен ). Будьте осторожны с именами файлов, содержащими пробелы или другие необычные символы!