Usuń wszystkie foldery w folderze oprócz jednego o określonej nazwie

18

Muszę usunąć wszystkie foldery w folderze za pomocą codziennego skryptu. Folder na ten dzień należy pozostawić.

Folder „myfolder” ma 3 podfoldery: „test1”, „test2” i „test3” Muszę usunąć wszystko oprócz „test2”.

Próbuję dopasować tutaj dokładną nazwę:

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

LUB

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

To polecenie zawsze próbuje również usunąć główny folder „myfolder”! Czy istnieje sposób, aby tego uniknąć?

Riju Mahna
źródło
6
W systemach Unix i Linux nazywamy te „katalogi”, a nie „foldery”.
tchrist
1
W zależności od powłoki może być konieczne zacytowanie tego !operatora: \!lub '!'.
Toby Speight,

Odpowiedzi:

32

Spowoduje to usunięcie wszystkich folderów w środku ./myfolderoprócz tego ./myfolder/test2i cała jego zawartość zostanie zachowana:

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

Jak to działa

  • find uruchamia polecenie znajdowania.
  • ./myfoldermówi find, aby rozpocząć od katalogu ./myfolderi jego zawartości.

  • -mindepth 1 nie pasować do ./myfoldersiebie, tylko pliki i katalogi pod nim.

  • ! -regex '^./myfolder/test2\(/.*\)?' mówi find, aby wykluczyć ( !) dowolny plik lub katalog pasujący do wyrażenia regularnego ^./myfolder/test2\(/.*\)?. ^dopasowuje początek nazwy ścieżki. Wyrażenie (/.*\)?odpowiada albo (a) ukośnikowi, po którym następuje cokolwiek, albo (b) w ogóle nic.

  • -delete każe znaleźć, aby usunąć pasujące (to znaczy niewykluczone) pliki.

Przykład

Rozważ strukturę katalogów, która wygląda;

$ 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

Możemy uruchomić polecenie find (bez -delete), aby zobaczyć, co pasuje:

$ 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

Możemy sprawdzić, czy to zadziałało, sprawdzając pozostałe pliki:

$ find ./myfolder
./myfolder
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2
John1024
źródło
1
Alternatywą jest -prunepozostawienie test2/*/podkatalogów w spokoju: wróć do rm -ri dodaj -maxdepth 1.
Toby Speight
@Isaac OK. Gotowy. (Również +1 za doskonałą odpowiedź.)
John1024,
Świetna robota !, ale przepraszam: spowoduje to usunięcie wszystkich plików w środku ./myfolder. Potrzebujesz brakującego (IMvhO) tylko-type d dla katalogów .
NotAnUnixNazi
Ok, to powinno działać tak, jak chcesz:find ./myfolder -depth -mindepth 1 -maxdepth 1 -type d ! -regex '^./myfolder/test2\(/.*\)?'
NotAnUnixNazi
10

Za pomocą bash :

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

Przykład:

$ 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
Jeff Schaller
źródło
5

tl; dr

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

Usuń echo, jeśli jesteś zadowolony z listy plików.


Użycie -mindepth 1spowoduje, że główny katalog nie zostanie wybrany.

$ 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

Ale nie-not -name test2 pozwoli uniknąć podkatalogów wewnątrz :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

Aby to zrobić, potrzebujesz czegoś takiego jak śliwka:

$ 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

Ale nie używaj delete, jak to sugeruje, deptha to zacznie wymazywać z najdłuższej ścieżki:

$ 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

Albo użyj rm -rf(usuń, echojeśli chcesz usunąć):

$ 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

Lub użyj również,maxdepth jeśli wszystko, czego potrzebujesz, to usunąć katalogi (i wszystko w środku) (usuń, echoaby faktycznie usunąć):

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

A -deletenadal nie powiedzie się, jeśli katalog nie jest pusty:

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -delete
find: cannot delete ‘./myfolder/test’: Directory not empty
NotAnUnixNazi
źródło
2

Jeśli używasz zsh, możesz:

setopt extended_glob # if you don't have it enabled

rm -rf myfolder/^test2
JoL
źródło
0

Przetestowano za pomocą poniższego polecenia i działało dobrze

find  /home/myfolder -maxdepth 1 -type d ! -iname test2 -exec rm -rvf {} \;
Praveen Kumar BS
źródło
Napotkałeś ten sam problem co OP; listowanie / home / folder w wierszu poleceń (bez krytycznego -mindepth 1) powoduje, że ten górny katalog spełnia wszystkie kryteria (jest to katalog i nie ma nazwy „test2”), więc zostaje usunięty.
Jeff Schaller