Удалить все файлы/каталоги, кроме указанных, на нескольких уровнях

Первый запуск xdg-screensaver activate. Если это сработает, возможно

  • у вас возникли проблемы с отправкой ввода на терминал
  • возможно, что-то вроде VLC блокирует заставку.

2
24.02.2020, 12:06
2 ответа

Можно использовать (отрицательный)-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.

1
28.04.2021, 23:22

Для такой потенциально разрушительной операции я бы сделал это осторожно, вручную :получив список всех файлов с полными путями; отредактируйте файлы, которые должны остаться (grep -v, или с помощью вашего любимого текстового редактора ); проверить, чтобы ничего не проскользнуло; дайте списокrm(возможно, потребуется использовать xargs(1), если список огромен ). Будьте осторожны с именами файлов, содержащими пробелы или другие необычные символы!

1
28.04.2021, 23:22

Теги

Похожие вопросы