
У меня следующая проблема. У меня есть структура каталогов, и я хочу удалить все, что находится в каталоге с именем 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 Snapshots
integrity. Если это проблема, вместо этого сделайте:
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 -n
with -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