Skutecznie usuń duży katalog zawierający tysiące plików

159

Mamy problem z nieporęcznym folderem z setkami tysięcy małych plików.

Jest tak wiele plików, że wykonanie rm -rfzwraca błąd, a zamiast tego musimy zrobić coś takiego:

find /path/to/folder -name "filenamestart*" -type f -exec rm -f {} \;

Działa to, ale jest bardzo wolne i ciągle kończy się brak pamięci.

Czy jest na to lepszy sposób? Idealnie chciałbym usunąć cały katalog bez dbania o zawartą w nim zawartość.

Toby
źródło
16
rm -rf *w folderze prawdopodobnie nie udaje się z powodu zbyt wielu argumentów; ale co rm -rf folder/jeśli chcesz usunąć cały katalog?
sr_
4
Zamiast usuwać go ręcznie, sugeruję umieszczenie folderu na osobnej partycji i po prostu odmontowanie & & format && remount.
bbaja42
7
Po prostu z ciekawości - ile plików trzeba przerwać rm -rf?
jw013
7
Prawdopodobnie powinieneś zmienić nazwę pytania na coś bardziej dokładnego, na przykład „Skutecznie usuń duży katalog zawierający tysiące plików”. Aby usunąć katalog i jego zawartość, rekursja jest z definicji konieczna. Można ręcznie odłączyć tylko sam i-węzeł katalogu (prawdopodobnie wymaga uprawnień roota), odmontować system plików i uruchomić fsckgo, aby odzyskać nieużywane bloki dysku, ale takie podejście wydaje się ryzykowne i może nie być szybsze. Ponadto sprawdzenie systemu plików może obejmować rekurencyjne przechodzenie przez drzewo systemu plików.
jw013
4
Kiedy miałem ccachetak ogromne drzewo plików i rmtrwało to tak długo (powodując spowolnienie całego systemu), znacznie szybciej było skopiować wszystkie inne pliki z systemu plików, sformatować je i skopiować z powrotem. Od tamtej pory tak masywne małe drzewa plików mają własny dedykowany system plików, więc możesz mkfsbezpośrednio zamiast rm.
frostschutz

Odpowiedzi:

211

Korzystanie z rsync jest zaskakująco szybkie i proste.

mkdir empty_dir
rsync -a --delete empty_dir/    yourdirectory/

Odpowiedź @ Sarath wspomniała o innym szybkim wyborze: Perl! Jego testy porównawcze są szybsze niż rsync -a --delete.

cd yourdirectory
perl -e 'for(<*>){((stat)[9]<(unlink))}'

Źródła:

  1. https://stackoverflow.com/questions/1795370/unix-fast-remove-directory-for-cleaning-up-daily-builds
  2. http://www.slashroot.in/which-is-the-fastest-method-to-delete-files-in-linux
Stevendaniels
źródło
4
Dzięki, bardzo przydatna. Cały czas używam rsync, nie miałem pojęcia, że ​​możesz go użyć do usunięcia w ten sposób. Znacznie szybszy niż rm -rf
John Powell
22
rsyncmoże być szybszy niż zwykły rm, ponieważ gwarantuje usunięcie w prawidłowej kolejności, więc potrzeba mniej obliczeń btress. Zobacz tę odpowiedź serverfault.com/a/328305/105902
Marki555
7
Czy ktokolwiek może zmodyfikować wyrażenie perla, aby rekurencyjnie usuwać wszystkie katalogi i pliki w katalogu id_be_be_deleted ?
Abhinav,
5
Uwagi: dodaj -Popcję do rsync, aby wyświetlić więcej, a także uważaj na składnię, ukośniki końcowe obowiązkowe. Na koniec możesz uruchomić polecenie rsync po raz pierwszy z -nopcją pierwszego uruchomienia suchego uruchomienia .
Drasill,
1
-arówna się -rlptgoD, ale tylko do usunięcia -rdjest konieczne
Koen.
38

Ktoś na Twitterze zasugerował użycie -deletezamiast-exec rm -f{} \;

To poprawiło efektywność polecenia, ale wciąż używa rekurencji, aby przejść przez wszystko.

Toby
źródło
11
To jest niestandardowe. GNU findma -deletei findmoże inne .
enzotib
13
-delete-exec rmze względów bezpieczeństwa i wydajności należy zawsze preferować, gdy są dostępne.
jw013
6
GNU jest de facto standardem.
RonJohn,
17

Co powiesz na coś takiego: find /path/to/folder -name "filenamestart*" -type f -print0 | xargs -0rn 20 rm -f

Możesz ograniczyć liczbę plików do usunięcia jednocześnie, zmieniając argument parametru -n. Uwzględniono także nazwy plików ze spacjami.

digital_infinity
źródło
2
Prawdopodobnie nie potrzebujesz -n 20bitu, ponieważ xargs i tak powinien ograniczyć się do akceptowalnych rozmiarów list argumentów.
Bezużyteczne
Tak masz rację. Oto notatka man xargs: (...) max-chars characters per command line (...). The largest allowed value is system-dependent, and is calculated as the argument length limit for exec. Tak -njest opcja dla takich przypadków, w których xargs nie może określić rozmiaru bufora CLI lub jeśli wykonane polecenie ma pewne ograniczenia.
digital_infinity
12

Sprytna sztuczka:

rsync -a --delete empty/ your_folder/

Jest bardzo intensywny, ale bardzo szybki. Zobacz https://web.archive.org/web/20130929001850/http://linuxnote.net/jianingy/en/linux/a-fast-way-to-remove-huge-number-of-files.html

MZAweb
źródło
Nie jest tak szybki, ponieważ odczytuje zawartość katalogu w nieefektywny sposób. Zobacz tę odpowiedź, aby uzyskać 10-krotnie szybsze rozwiązanie i wyjaśnienie serverfault.com/a/328305/105902
Marki555
2
@ Marki555: w edycji pytania jest zgłaszane 60 sekund dla rsync -a --deletevs 43 dla lsdent. Współczynnik 10x był dla time ls -1 | wc -l vs time ./dentls bigfolder >out.txt(to jest częściowo rzetelne porównanie z powodu > filevs wc -l).
Hastur
Problem polega na tym, że ŻADNE z tamtych poleceń NIE WYKONAJ żądanej operacji przejścia w celu usunięcia. Podają kod? NIE DZIAŁA w sposób opisany przez Marki555.
Svartalf,
11

Rozwijając jeden z komentarzy, nie sądzę, że robisz to, co myślisz.

Najpierw stworzyłem ogromną liczbę plików, aby zasymulować twoją sytuację:

$ mkdir foo
$ cd foo/
$ for X in $(seq 1 1000);do touch {1..1000}_$X; done

Potem spróbowałem tego, czego się nie spodziewałem i co brzmi jak robisz w pytaniu:

$ rm -r foo/*
bash: /bin/rm: Argument list too long

Ale to czyni pracę:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory
Izkata
źródło
6
Jest to jedyne działające rozwiązanie: Uruchom rm -Rf bigdirectorykilka razy. Miałem katalog z tysiącami milionów podkatalogów i plików. Nie mogłem nawet uruchomić lslub findczy rsyncw tym katalogu, ponieważ zabrakło pamięci. Polecenie rm -Rfkilkakrotnie kończyło pracę (brak pamięci), usuwając tylko część miliardów plików. Ale po wielu próbach w końcu zadziałało. Wydaje się być jedynym rozwiązaniem, jeśli problemem jest brak pamięci.
erik
6

Miałem okazję przetestować -deletew porównaniu do -exec rm \{\} \;i dla mnie -deletebyła odpowiedzią na ten problem.

Za pomocą -deleteusuniętych plików w folderze 400 000 plików co najmniej 1000 razy szybciej niż rm.

Artykuł „Jak usunąć dużą liczbę plików w systemie Linux” sugeruje, że jest on około trzy razy szybszy, ale w moim teście różnica była znacznie bardziej dramatyczna.

użytkownik2365090
źródło
3
Użycie powoduje find -execwykonanie rmpolecenia dla każdego pliku osobno, dlatego jest tak wolny.
Marki555
5

O -deletepowyższej opcji: używam jej do usuwania dużej liczby (1M + est) plików w folderze tymczasowym, który utworzyłem i mimowolnie zapomniałem co noc czyścić. Przypadkowo zapełniłem mój dysk / partycję i nic więcej nie mogło ich usunąć oprócz find .polecenia. Jest wolny, początkowo używałem:

find . -ls -exec rm {} \;

Ale zajęło to NIEZWYKLE czasu. Zaczęło się po około 15 minutach, aby usunąć niektóre pliki, ale domyślam się, że usuwało mniej niż 10 na sekundę po tym, jak w końcu się zaczęło. Próbowałem więc:

find . -delete

zamiast tego i pozwalam mu działać teraz. Wygląda na to, że działa szybciej, chociaż BARDZO obciąża procesor, czego nie było inne polecenie. Działa już od godziny i myślę, że odzyskuję miejsce na dysku, a partycja stopniowo „odchudza się”, ale wciąż zajmuje to bardzo dużo czasu. Poważnie wątpię, że działa 1000 razy szybciej niż inne. Jak we wszystkich rzeczach, chciałem tylko zwrócić uwagę na kompromis w przestrzeni w czasie. Jeśli masz do dyspozycji przepustowość procesora (my), uruchom drugą. Uruchomił mój procesor ( uptimeraporty):

10:59:17 up 539 days, 21:21,  3 users,  load average: 22.98, 24.10, 22.87

Zauważyłem, że średnia wartość obciążenia przekracza 30,00, co nie jest dobre dla zajętego systemu, ale dla naszego, który jest zwykle lekko obciążony, jest OK przez kilka godzin. Sprawdziłem większość innych rzeczy w systemie i one wciąż reagują, więc na razie jesteśmy w porządku.

Scotty
źródło
jeśli zamierzasz użyć exec, prawie na pewno nie chcesz używać -lsi robienie find . -type f -exec rm '{}' ++ jest szybsze, ponieważ da to tyle argumentów do rm, ile może obsłużyć naraz.
ksenoterrakid
Myślę, że powinieneś to zrobić i edytować to we własnej odpowiedzi… to naprawdę za długo na komentarz. Wygląda też na to, że twój system plików ma dość drogie usuwanie, ciekawe, który to jest? Możesz to uruchomić find … -deleteprzez nicelub ionice, co może pomóc. Może więc zmienić niektóre opcje montowania na ustawienia mniej odporne na awarie. (I, oczywiście, w zależności od tego, co jeszcze jest w systemie plików, często najszybszym sposobem na usunięcie wszystkiego mkfs.)
derobert
3
Przeciętne obciążenie nie zawsze jest procesorem, jest tylko miarą liczby zablokowanych procesów w czasie. Procesy mogą blokować dyskowe operacje we / wy, co prawdopodobnie dzieje się tutaj.
Score_Under
Należy również pamiętać, że średnia obciążenia nie uwzględnia liczby logicznych procesorów. Tak więc loadavg 1dla maszyny jednordzeniowej jest taki sam jak loadavg 64w systemie 64-rdzeniowym - co oznacza, że ​​każdy procesor jest zajęty przez 100% czasu.
Marki555
3

Istnieje kilka metod usuwania dużej liczby plików w systemie Linux. Możesz użyć opcji Znajdź z opcją usuwania, która jest szybsza niż opcja exec. Następnie możesz użyć perla unlink, a nawet rsync. Jak usunąć dużą liczbę plików w systemie Linux

sarath
źródło
3

Rozważ użycie woluminu Btrfs i po prostu usuń cały wolumin dla takiego katalogu z dużą liczbą plików.

Alternatywnie możesz utworzyć plik obrazu FS, a następnie odmontować i usunąć jego plik, aby usunąć wszystko na raz naprawdę szybko.

Siergiej
źródło
2

Zakładając, że mam parallelzainstalowany GNU , użyłem tego:

parallel rm -rf dir/{} ::: `ls -f dir/`

i było wystarczająco szybko.

Nacho
źródło
1

Usuwanie NAPRAWDĘ DUŻYCH katalogów wymaga innego podejścia, jak dowiedziałem się z tej strony - będziesz musiał użyć ionice. Zapewnia to (przy -c3), że usuwanie zostanie wykonane tylko wtedy, gdy system ma na to czas IO. Obciążenie systemu nie wzrośnie i wszystko pozostanie responsywne (chociaż mój czas procesora na znalezienie był dość wysoki i wynosił około 50%).

find <dir> -type f -exec ionice -c3 rm {} \;
gamma
źródło
5
użycie +zamiast \;sprawiłoby, że byłoby to szybsze, ponieważ przekazuje więcej argumentów do rm naraz, mniej rozwidlenia
ksenoterracid
1
Dlaczego nie ionice -c3 find <dir> -type f -delete
jtgd
0
ls -1 | xargs rm -rf 

powinien działać w folderze głównym

PsyStyle
źródło
1
lsnie będzie działać z powodu ilości plików w folderze. Właśnie dlatego musiałem skorzystać find, dzięki.
Toby
4
@Toby: Spróbuj ls -f, co wyłącza sortowanie. Sortowanie wymaga załadowania całego katalogu do pamięci w celu posortowania. Niesortowane lspowinny być w stanie przesyłać strumieniowo swoje dane wyjściowe.
camh
1
Nie działa w przypadku nazw plików zawierających znaki nowej linii.
maxschlepzig
@camh to prawda. Ale usuwanie plików w posortowanej kolejności jest szybsze niż nieposortowane (z powodu ponownego obliczenia btree katalogu po każdym usunięciu). Zobacz tę odpowiedź na przykład serverfault.com/a/328305/105902
Marki555
@maxschlepzig dla takich plików, których możesz użyć find . -print0 | xargs -0 rm, który użyje znaku NULL jako separatora nazw plików.
Marki555
0

Za wskazówkę Izkaty powyżej:

Ale to czyni pracę:

$ rm -r foo/
$ ls foo
ls: cannot access foo: No such file or directory

To prawie działało - lub by działało - ale miałem pewne problemy z pozwoleniem; pliki znajdowały się na serwerze, ale nadal nie rozumiem, skąd wziął się ten problem z uprawnieniami. W każdym razie Terminal poprosił o potwierdzenie każdego pliku. Liczba plików wynosiła około 20 000, więc nie było takiej opcji. Po „-r” dodałem opcję „-f”, więc całe polecenie brzmiało „ rm -r -f nazwa_folderu / ”. Potem wydawało się, że działa dobrze. Jestem nowicjuszem w Terminalu, ale myślę, że to było w porządku, prawda? Dzięki!

użytkownik41527
źródło
0

W zależności od tego, jak dobrze musisz pozbyć się tych plików, sugeruję użycie shred.

$ shred -zuv folder

jeśli chcesz wyczyścić katalog, ale nie możesz go usunąć i odtworzyć, sugeruję przeniesienie go i odtworzenie go natychmiast.

mv folder folder_del
mkdir folder
rm -rf folder_del

jest to szybsze, wierzcie lub nie, ponieważ trzeba zmienić tylko jeden i-węzeł. Pamiętaj: tak naprawdę nie można zrównoważyć tego smaku na komputerze wielordzeniowym. Sprowadza się to do dostępu do dysku, który jest ograniczony przez macierz RAID lub to, co masz.

polemon
źródło
1
shred nie będzie działać z wieloma nowoczesnymi systemami plików.
0

Jeśli masz miliony plików i każde powyższe rozwiązanie powoduje stres w systemie, możesz spróbować inspiracji:

Plik nice_delete:

#!/bin/bash

MAX_LOAD=3
FILES=("$@")
BATCH=100

while [ ${#FILES[@]} -gt 0 ]; do
    DEL=("${FILES[@]:0:$BATCH}")
    ionice -c3 rm "${DEL[@]}"
    echo -n "#"
    FILES=("${FILES[@]:$BATCH}")
    while [[ $(cat /proc/loadavg | awk '{print int($1)}') -gt $MAX_LOAD ]]; do
        echo -n "."
        sleep 1
    done
done

A teraz usuń pliki:

find /path/to/folder -type f -exec ./nice_delete {} \+

Find utworzy partie (patrz getconf ARG_MAX) kilkudziesięciu tysięcy plików i przekaże je nice_delete. Spowoduje to utworzenie jeszcze mniejszych partii, aby umożliwić spanie po wykryciu przeciążenia.

brablc
źródło
0

Jeśli chcesz pozbyć się wielu plików tak szybko, jak to możliwe, ls -f1 /path/to/folder/with/many/files/ | xargs rmmoże działać dobrze, ale lepiej nie uruchamiaj ich w systemach produkcyjnych, ponieważ w systemie mogą wystąpić problemy z operacjami we / wy, a aplikacje mogą utknąć podczas operacji usuwania.

Ten skrypt działa dobrze dla wielu plików i nie powinien wpływać na ioload systemu.

#!/bin/bash

# Path to folder with many files
FOLDER="/path/to/folder/with/many/files"

# Temporary file to store file names
FILE_FILENAMES="/tmp/filenames"

if [ -z "$FOLDER" ]; then
    echo "Prevented you from deleting everything! Correct your FOLDER variable!"
    exit 1
fi

while true; do
    FILES=$(ls -f1 $FOLDER | wc -l)
    if [ "$FILES" -gt 10000 ]; then
        printf "[%s] %s files found. going on with removing\n" "$(date)" "$FILES"
        # Create new list of files
        ls -f1 $FOLDER | head -n 5002 | tail -n 5000 > "$FILE_FILENAMES"

        if [ -s $FILE_FILENAMES ]; then
            while read FILE; do
                rm "$FOLDER/$FILE"
                sleep 0.005
            done < "$FILE_FILENAMES"
        fi
    else
        printf "[%s] script has finished, almost all files have been deleted" "$(date)"
        break
    fi
    sleep 5
done
Leon Kramer
źródło