Usuń puste podfoldery, zachowaj folder nadrzędny

15

Kiedy używam

find /home/user/parentdir -type d -empty -delete

szuka rekurencyjnie pustych podfolderów wewnątrz /home/user/parentdiri usuwa je. Ale jeśli /home/user/parentdirjest również pusty, usuwa również parentdirfolder, co jest dla mnie niepożądane.

Chcę zachować to parentdirw rsyncniektórych plikach do utworzenia kopii zapasowej lub w chmurze. Po przetworzeniu muszę usunąć puste foldery, ale ich odtwarzanie za parentdirkażdym razem wydaje się bezproduktywne .

Wszelkie sugestie do zachowania parentdir? Myślałem o utworzeniu w nim .nocopypliku i wykluczeniu go rsync, ale wygląda to na przesadę. Czy istnieje bardziej elegancki sposób?

TNT
źródło
jeśli dodasz ukośnik do przodu / na końcu / parentdir (tj. / parentdir /), czy to robi różnicę?
Graham
6
-mindepth 1?
steeldriver
@Graham / parentdir / usuwa także parentdir, więc nie ma znaczenia.
TNT
ah, widzę, że przegapiłem * na końcu, o którym @Amourk wspomina w swojej odpowiedzi.
Graham

Odpowiedzi:

25

Po prostu zrób find /home/user/parentdir -mindepth 1 -type d -empty -delete.

Popatrz:

$ mkdir -p test1/test2
$ find test1 -type d
test1
test1/test2
$ find test1 -mindepth 1  -type d
test1/test2

Odpowiedź find /home/user/parentdir/* w AmourK jest niepożądana, gdy jest dużo plików i jest nadmiernie skomplikowana.

chx
źródło
15

Dodając /*do końca parentdir, wykonujesz akcję na wszystkich podkatalogach parentdir, a nie na parentdirsobie. I w ten sam sposób /home/user/nie zostanie usunięty ze starego polecenia, parentdirnie zostanie usunięty z polecenia poniżej. *jest nazywany operatorem globalnym i pasuje do dowolnego ciągu znaków.

find /home/user/parentdir/* -type d -empty -delete

AmourK
źródło
7
Jedną z rzeczy, o których należy pamiętać przy takim podejściu, jest to, że jeśli w pliku znajduje się duża liczba plików /home/user/parentdir/, rozwinięty glob może przekroczyć ARG_MAX, co powoduje, że lista argumentów jest zbyt długa . Możesz zmniejszyć ryzyko takiego zdarzenia, zmieniając glob na */tak, aby pasował tylko do katalogów.
steeldriver
12
Pamiętaj też, że nie znajdzie to dzieci rozpoczynających się od kropki. I pewnego dnia zdasz sobie z tego sprawę, a jeśli również znajdziesz szukaną frazę „. *”, Znajdziesz się w ogromnym świecie bólu (ponieważ „. *” Pasuje do „..”). Zapytaj mnie, skąd to wiem.
Glenn Willen
1
@GlennWillen, dlaczego uważam, że powinniśmy siedzieć w barze z napojami przed nami, zanim poproszę o opowiedzenie tej historii?
Monty Harder
@MontyHarder Na szczęście nie było tak źle, jak mogło być. Był na moim pierwszym systemie Unix, który był maszyną Solaris, należącą do mojej szkoły średniej. Dzięki Bogu, że nie uruchomiłem „rm”, ale „chown”. Użyłem roota, aby naprawić własność wszystkiego w moim katalogu domowym. Zamiast tego oddałem się w posiadanie KAŻDEGO katalogu domowego (i całej ich zawartości), a następnie musiałem je wszystkie przywrócić.
Glenn Willen
Miałem współpracownika, który szkolił nowego pracownika w zakresie instalowania oprogramowania. Polegało to na przejściu do /tmp, utworzeniu podkatalogu, rozpakowaniu tam skompresowanego archiwum, uruchomieniu instalatora i oczyszczeniu. Nowicjusz pomyślał, że rm -rf /tmp/foo-installjest to właściwy sposób, a doświadczona ręka powiedziała „Noooooo” w swoim najlepszym wrażeniu Darth-Vadera na końcu odcinka III, newb dowiedział się, jak blisko jest / i klawisze Enter były do ​​siebie. Musieliśmy uruchomić system z dysku przywracania oprogramowania do tworzenia kopii zapasowych i ponownie załadować cały serwer z taśmy.
Monty Harder
0

jeśli masz zainstalowane php-cli,

printf %s $(pwd) | php -r 'function f(string $dir){var_dump($dir);$dir.=DIRECTORY_SEPARATOR;foreach(glob("$dir*",GLOB_ONLYDIR) as $d){f($d);}global $original;if(substr($dir,0,-strlen(DIRECTORY_SEPARATOR))!==$original && empty(glob("$dir*"))){rmdir($dir);}}f(($original=stream_get_contents(STDIN)));'
hanshenrik
źródło
chciałeś foreach (RecursiveIteratorIterator(RecursiveDirectoryIterator($dir)) as $p) if (empty($skipped)) $skipped = TRUE; elseif ($p->isDir()) @rmdir($p->getPathname());lub coś podobnego. $skippedzajmuje się pomijaniem pierwszego, @rmdirpróbuje usunąć katalog i po cichu (to jest @) kończy się niepowodzeniem, jeśli nie jest puste.
chx
0

Gdy potrzebujesz tylko jednego poziomu (pozostawienie folderu nadrzędnego, ale usunięcie pustych folderów podrzędnych), możesz zastosować łatwą sztuczkę rmdir.

rmdir parent/*

Usuwa wszystkie puste podfoldery, ale drukuje tylko błąd plików i niepustych folderów.

Nie działa to rekurencyjnie, ale znajomość struktury folderów może być najszybszym sposobem na zrobienie czegoś takiego

rmdir parent/*/*/* # deletes parent/a/b/c when empty
rmdir parent/*/* # deletes parent/a/b when it is now empty
rmdir parent/* # deletes parent/a when it is now empty

Zaleta: łatwy do zapamiętania, szybki w pisaniu, mały potencjał wyboru niewłaściwego parametru.
Wada: mniej elastyczny, prawdopodobnie dużo komunikatów o błędach w twoim terminalu.

allo
źródło