Используйте find, чтобы найти определенный каталог и удалить все файлы в нем, кроме одного каталога.

Используйте find, чтобы найти определенный каталог и удалить все файлы в нем, кроме одного каталога.

У меня следующая проблема. У меня есть структура каталогов, и я хочу удалить все, что находится в каталоге с именем Caches. Все должно быть удалено, кроме всех каталогов с именем Snapshots. Я не могу понять, как это сделать с помощьюнаходитьили любую другую известную мне команду.

Причина, по которой я спрашиваю: в iOS каждое приложение имеет свой собственный каталог кэшей. Иногда они не очищаются должным образом, в зависимости от приложения. С решением этого ответа можно было бы очистить кэши в iOS, и, следовательно, оптимизировать дисковое пространство, когда диск устройства смонтирован на другом компьютере, например, с помощью FUSE (iExplorer).

Вот что у меня есть на данный момент:

find . 2>/dev/null -type d -name "Caches" -maxdepth 3 -print

Это возвращает что-то вроде:

./Library/Caches

Когда я делаю это, ls ./Library/Cachesя вижу все содержимое и Snapshotsкаталог, который я хочу исключить, потому что в конечном итоге мне нужно -deleteвсе, кроме этого.


Я хочу что-то вроде этого:

  Before:                            After:

  .                                  .
  ├── a                              ├── a
  │   ├── a                          │   ├── a
  │   └── Caches                     │   └── Caches
  │       ├── a                      │       └── a
  │       │   └── Snapshots          │           └── Snapshots
  │       │       └── a              │               └── a
  │       ├── b                      └── b
  │       │   └── a                      └── c
  │       └── c                  
  └── b
      ├── c
      └── Caches
          ├── a
          │   └── foo
          │       └── a
          └── b
              └── a

решение1

Меня немного смущает формулировка вопроса. Если вы хотите удалить все в каталоге, ./Library/Cachesкроме одной папки с именем Snapshots, нет необходимости использовать find. В bash, shell globs — самый простой способ:

shopt -s extglob  # probably already enabled
echo Library/Caches/!(Snapshots)

Если при этом будут выведены все файлы/каталоги, которые вы хотите удалить, замените их echoна rm -f --.

SnapshotsЕсли на разных уровнях дерева каталогов ниже имеется несколько каталогов ./Library/Caches, которые вы хотите сохранить, то с помощью GNU findвы можете сделать следующее:

find Library/Caches ! -path '*Snapshots*'

Это должно вывести все файлы/каталоги, за исключением тех, которые содержатся Snapshots в их пути. Это включит каталоги, которые содержат (или чьи потомки содержат) Snapshotsкаталоги, однако find ... -deleteне удалит их, а вместо этого выведет ошибку, что они не пустые. Когда вы будете довольны, добавьте -deleteв конец.

Одно предостережение заключается в том, что это оставит любойфайлыnamed Snapshotsintegrity. Если это проблема, вместо этого сделайте:

find Library/Caches \( ! -path '*Snapshots*' -o -type f -name Snapshots \)

Снова добавьте -delete, когда будете довольны.

решение2

find . -depth -print0 | perl -0lne '
  if ("$_/" =~ m{/Caches(/.*)}s && $1 !~ m{/Snapshots/}) {rmdir $_ or unlink $_}'

Если ваш findне поддерживает -print0, вы можете заменить его на -exec printf '%s\0' {} +.

Идея состоит в том, чтобы вывести список файлов, завершающихся нулем (поскольку 0 — единственный байт, который не может встречаться в пути к файлу), и использовать параметр perl's -nwith -0для запуска некоторого кода для каждого из этих имен файлов (с $_заданным именем файла).

С -depth, файлы печатаются перед их родительским каталогом. Мы удаляем только файлы или каталоги (предполагая, что они пусты, поэтому важно обрабатывать список по depthпорядку), если их путь содержит , /Caches/за которым не следует /Snapshosts/.

решение3

Я не findколдун, поэтому не хочу сказать, что вы не можете сделать это с помощью find, но я знаю, что это можно сделать с помощью пары строк bash. Учитывая, что я правильно понимаю вашу структуру каталогов:

#!/bin/bash

for i in Library/Caches/*; do
    if [[ -d "$i" ]]; then
         [[ "$i" =~ "Snapshots" ]] ||  echo "rm-ing  $i" # change to rm -rf "$i" to use
    fi
done

Оператор forвозвращает каждый элемент в Library/Caches/. if [[ -d "$i" ]]Оператор проверяет, является ли каждый элемент каталогом, и если это так, то его имя проверяется на наличие в нем «Snapshots», [[ "$i" =~ "Snapshots" ]]. Поскольку нет оператора для отрицания соответствия регулярному выражению, как !=~, я использовал ||для выполнения, rmесли предыдущая команда не удалась, например, имя каталога не содержит Snapshots.

Это будет работать на любой современной системе, использующей bash, но она должна иметь bash. Также вам может понадобиться поиграться со структурой каталогов, но этого должно быть достаточно.

решение4

Первая часть: найти каталоги с именами caches и что-нибудь с ними сделать

find . -name Caches -type d -exec … \;

Вторая часть: пройти по каталогу кэша и удалить все, но сохранить Snapshotsподкаталоги и их содержимое (а также пути, ведущие к ним). Выполнить обход в глубину, чтобы каталоги можно было удалить, как только они станут пустыми.

    find /path/to/Caches -depth -name Snapshots -type d -prune \
                             -o \( -type d -empty -o ! -type d \) -delete

Теперь объедините их вместе.

find . -name Caches -type d -exec
    find {} -depth -name Snapshots -type d -prune \
                -o \( -type d -empty -o ! -type d \) -delete

Связанный контент