Удалить все папки внутри папки, кроме одной с определенным именем


17

Мне нужно удалить все папки внутри папки, используя ежедневный скрипт. Папку на этот день нужно оставить.

Папка «myfolder» имеет 3 подпапки: «test1», «test2» и «test3». Мне нужно удалить все, кроме «test2».

Я пытаюсь найти точное имя здесь:

find /home/myfolder -type d ! -name 'test2' | xargs rm -rf

ИЛИ

find /home/myfolder -type d ! -name 'test2' -delete

Эта команда всегда пытается удалить основную папку «myfolder»! Есть ли способ избежать этого?


6
В Unix и Linux мы называем эти «каталоги», а не «папки».
tchrist

1
В зависимости от вашей оболочки, вам может потребоваться указать этот !оператор: \!или '!'.
Тоби Спейт

Ответы:


32

Это удалит все папки внутри, ./myfolderкроме этого, ./myfolder/test2и все его содержимое будет сохранено:

find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?' -delete

Как это устроено

  • find запускает команду поиска
  • ./myfolderговорит find начать с каталога ./myfolderи его содержимого.

  • -mindepth 1 не соответствует ./myfolderсамому себе, только файлы и каталоги под ним.

  • ! -regex '^./myfolder/test2\(/.*\)?' говорит find для exclude ( !) любой файл или каталог, соответствующий регулярному выражению ^./myfolder/test2\(/.*\)?. ^соответствует началу имени пути. Выражение (/.*\)?соответствует либо (a) косой черте, за которой следует что-либо, либо (b) вообще ничего.

  • -delete говорит find для удаления соответствующих (то есть, не исключенных) файлов.

пример

Рассмотрим структуру каталогов, которая выглядит следующим образом;

$ find ./myfolder
./myfolder
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2

Мы можем запустить команду find (без -delete), чтобы увидеть, что она соответствует:

$ find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?'
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3

Мы можем убедиться, что это сработало, посмотрев на файлы, которые остались:

$ find ./myfolder
./myfolder
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2

1
Альтернатива, чтобы -pruneоставить test2/*/подкаталоги в покое: вернуться rm -rи добавить -maxdepth 1.
Тоби Спейт

@ Исаак ОК. Выполнено. (Также +1 за ваш отличный ответ.)
John1024

Отличная работа !, но извините: это удалит все файлы внутри ./myfolder. Вам нужно пропустить (IMvhO) только-type d для каталогов .
Исаак

Хорошо, это должно работать, как вы хотите:find ./myfolder -depth -mindepth 1 -maxdepth 1 -type d ! -regex '^./myfolder/test2\(/.*\)?'
Исаак

10

Используя bash :

shopt -s extglob
rm -r myfolder/!(test2)/

Пример:

$ tree myfolder/
myfolder/
├── test1
│   └── file1
├── test2
│   └── file2
└── test3
    └── file3

$ echo rm -r myfolder/!(test2)
rm -r myfolder/test1 myfolder/test3
$ rm -r myfolder/!(test2)
$ tree myfolder/
myfolder/
└── test2
    └── file2

1 directory, 1 file

5

ТЛ; др

find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 \
     -exec echo rm -rf '{}' \;

Удалите эхо, если удовлетворены списком файлов.


Использование -mindepth 1гарантирует, что верхний каталог не выбран.

$ find ./myfolder -mindepth 1 -type d
./myfolder/test2
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Но -not -name test2будет не избежать подкаталогов внутри test2:

$ find ./myfolder -mindepth 1 -type d -not -name 'test2'
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Для этого вам нужно что-то вроде чернослива:

$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Но не используйте delete, как это подразумевается, depthи это начнет стираться с самого длинного пути:

$ find ./myfolder -depth -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test/a1/a2/a3
./myfolder/test/a1/a2
./myfolder/test/a1
./myfolder/test

Либо используйте rm -rf(удалите, echoесли вы действительно хотите стереть):

$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test
rm -rf ./myfolder/test/a1
rm -rf ./myfolder/test/a1/a2
rm -rf ./myfolder/test/a1/a2/a3

Или, также используйте, maxdepthесли все, что вам нужно, это удалить каталоги (и все внутри) (удалить, echoчтобы фактически стереть):

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test

A -deleteвсе равно не будет работать, если каталог не пустой:

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -delete
find: cannot delete ‘./myfolder/test’: Directory not empty

2

Если вы используете zsh, вы можете:

setopt extended_glob # if you don't have it enabled

rm -rf myfolder/^test2

0

Протестировано с приведенной ниже командой, и она работала нормально

find  /home/myfolder -maxdepth 1 -type d ! -iname test2 -exec rm -rvf {} \;

Вы столкнулись с той же проблемой, что и ОП; Перечисление / home / folder в командной строке (без критического значения -mindepth 1) делает этот верхний каталог соответствующим всем критериям (это каталог, и он не называется «test2»), и поэтому он удаляется.
Джефф Шаллер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.