Próbuję znaleźć najlepszy sposób na znalezienie liczby plików w określonym katalogu, gdy jest ich bardzo dużo (> 100 000).
Gdy jest tak wiele plików, wykonanie operacji ls | wc -l
zajmuje dużo czasu. Myślę, że dzieje się tak, ponieważ zwraca nazwy wszystkich plików. Próbuję zająć jak najmniej IO dysku.
Bezskutecznie eksperymentowałem z niektórymi skryptami powłoki i Perla. Jakieś pomysły?
Odpowiedzi:
Domyślnie
ls
sortuje nazwy, co może chwilę potrwać, jeśli jest ich dużo. Nie będzie również wyjścia, dopóki wszystkie nazwy nie zostaną odczytane i posortowane. Użyjls -f
opcji, aby wyłączyć sortowanie.Należy pamiętać, że będzie to również włączyć
-a
, tak.
,..
i inne pliki zaczynające się.
będą zliczane.źródło
ls
.stat()
wywołanials
każdego pliku.find
nie,stat()
więc działa szybciej.ls -f
też niestat()
. Ale oczywiście obals
ifind
dzwonią,stat()
gdy używane są pewne opcje, takie jakls -l
lubfind -mtime
.ls -fR | wc -l
Najszybszym sposobem jest specjalny program, taki jak ten:
Z moich testów bez względu na pamięć podręczną, uruchomiłem każdy z nich około 50 razy w tym samym katalogu, w kółko, aby uniknąć wypaczenia danych w pamięci podręcznej, i otrzymałem z grubsza następujące dane dotyczące wydajności (w czasie rzeczywistym):
Ten ostatni
dircnt
to program skompilowany z powyższego źródła.EDYCJA 2016-09-26
Ze względu na powszechne zapotrzebowanie ponownie napisałem ten program tak, aby był rekurencyjny, więc będzie spadał do podkatalogów i nadal będzie oddzielnie liczył pliki i katalogi.
Ponieważ jest jasne, że niektórzy ludzie chcą wiedzieć, jak to wszystko zrobić, mam wiele komentarzy w kodzie, aby spróbować pokazać, co się dzieje. Napisałem to i przetestowałem na 64-bitowym Linuksie, ale powinno działać na każdym systemie zgodnym z POSIX, w tym Microsoft Windows. Raporty o błędach są mile widziane; Z przyjemnością zaktualizuję to, jeśli nie możesz go uruchomić w systemie AIX, OS / 400 lub czymkolwiek.
Jak widać, jest to o wiele bardziej skomplikowane niż oryginał i koniecznie tak: przynajmniej jedna funkcja musi istnieć, aby była wywoływana rekurencyjnie, chyba że chcesz, aby kod stał się bardzo złożony (np. Zarządzanie stosem podkatalogów i przetwarzanie go w pojedynczej pętli). Ponieważ musimy sprawdzać typy plików, w grę wchodzą różnice między różnymi systemami operacyjnymi, standardowymi bibliotekami itp., Dlatego napisałem program, który stara się być użyteczny w każdym systemie, w którym będzie się kompilował.
Jest bardzo mało sprawdzania błędów, a
count
sama funkcja tak naprawdę nie raportuje błędów. Jedyne wywołania, które naprawdę mogą zawieść, toopendir
istat
(jeśli nie masz szczęścia i masz system, w którym jużdirent
zawiera typ pliku). Nie mam paranoi na punkcie sprawdzania całkowitej długości nazw ścieżek podkatalogu, ale teoretycznie system nie powinien zezwalać na żadną nazwę ścieżki dłuższą niżPATH_MAX
. Jeśli są jakieś obawy, mogę to naprawić, ale jest to po prostu więcej kodu, które trzeba wyjaśnić osobie uczącej się pisać C. Ten program ma być przykładem tego, jak rekursywnie zagłębiać się w podkatalogi.EDYCJA 2017-01-17
Wprowadziłem dwie zmiany sugerowane przez @FlyingCodeMonkey:
lstat
zamiaststat
. Zmieni to zachowanie programu, jeśli w skanowanym katalogu znajdują się dowiązane symbolicznie katalogi. Poprzednie zachowanie polegało na tym, że do (połączonego) podkatalogu dodawano liczbę plików do ogólnej liczby; nowe zachowanie polega na tym, że połączony katalog będzie liczony jako pojedynczy plik, a jego zawartość nie będzie liczona.EDYCJA 2017-06-29
Przy odrobinie szczęścia będzie to ostatnia edycja tej odpowiedzi :)
Skopiowałem ten kod do repozytorium GitHub, aby nieco łatwiej było uzyskać kod (zamiast kopiować / wklejać, możesz po prostu pobrać źródło ), a ponadto ułatwia każdemu zaproponowanie modyfikacji poprzez przesłanie pull -request z GitHub.
Źródło jest dostępne na licencji Apache License 2.0. Łatki * mile widziane!
źródło
gcc -o dircnt dircnt.c
i użyj tak./dircnt some_dir
Czy próbowałeś znaleźć? Na przykład:
źródło
find /usr/share | wc -l
(~ 137 000 plików) jest około 25% szybsze niżls -R /usr/share | wc -l
(~ 160 000 wierszy, w tym nazwy katalogów , sumy katalogów i puste wiersze) przy pierwszym uruchomieniu każdego z nich i co najmniej dwa razy szybsze podczas porównywania kolejnych (buforowanych) uruchomień.find
jest szybsza niż zls
powodu tego, jak używaszls
. Jeśli przerwiesz sortowaniels
i uzyskaszfind
podobną wydajność.find, ls i perl przetestowano na 40 000 plików: ta sama prędkość (chociaż nie próbowałem wyczyścić pamięci podręcznej):
i z perl opendir / readdir, w tym samym czasie:
uwaga: użyłem / bin / ls -f, aby upewnić się, że omijam opcję aliasu, która może trochę spowolnić, oraz -f, aby uniknąć porządkowania plików. ls bez -f jest dwukrotnie wolniejsze niż find / perl, z wyjątkiem tego, że jeśli ls jest używane z -f, wydaje się, że jest to ten sam czas:
Chciałbym również mieć skrypt, który bezpośrednio zadaje system plików bez wszystkich niepotrzebnych informacji.
testy oparte na odpowiedzi Petera van der Heijdena, Glenna Jackmana i mark4o.
Tomasz
źródło
ls -l | wc -l
na folderze na zewnętrznym dysku twardym 2,5 "z 1 MB plików, operacja trwa około 3 minut. Za drugim razem zajmuje to 12 sekund IIRC. Może to również potencjalnie zależeć od systemu plików. I używałBtrfs
.$ time perl -e 'opendir D, "."; @files = readdir D; closedir D; print scalar(@files)."\n"' 1315029 real 0m0.580s user 0m0.302s sys 0m0.275s
Możesz zmienić dane wyjściowe w zależności od swoich wymagań, ale oto jeden wiersz bash, który napisałem, aby rekurencyjnie zliczać i raportować liczbę plików w serii katalogów nazwanych numerycznie.
To wyszukuje rekurencyjnie wszystkie pliki (nie katalogi) w podanym katalogu i zwraca wyniki w formacie podobnym do skrótu. Proste poprawki w poleceniu wyszukiwania mogą sprawić, że typy plików, które chcesz policzyć, będą bardziej szczegółowe itp.
Daje coś takiego:
źródło
ls -1 ${dir}
nie będzie działać poprawnie bez większej ilości spacji. Ponadto nie ma gwarancji, że nazwa zwrócona przezls
może zostać przekazanafind
, ponieważls
nie zawiera znaków niedrukowalnych do spożycia przez ludzi. (mkdir $'oddly\nnamed\ndirectory'
jeśli chcesz szczególnie interesującego przypadku testowego). Zobacz Dlaczego nie powinieneś analizować wyniku ls (1)Zaskakująco dla mnie znalezisko gołej kości jest bardzo podobne do ls -f
przeciw
Oczywiście wartości na trzecim miejscu po przecinku przesuwają się nieco za każdym razem, gdy wykonujesz którekolwiek z nich, więc są w zasadzie identyczne. Zwróć jednak uwagę, że
find
zwraca jedną dodatkową jednostkę, ponieważ zlicza sam katalog (i, jak wspomniano wcześniej,ls -f
zwraca dwie dodatkowe jednostki, ponieważ liczy również… i…).źródło
Dodam to tylko ze względu na kompletność. Prawidłowa odpowiedź została oczywiście wysłana przez kogoś innego, ale możesz również uzyskać liczbę plików i katalogów za pomocą programu drzewiastego.
Uruchom polecenie,
tree | tail -n 1
aby uzyskać ostatnią linię, która powie coś w rodzaju „763 katalogów, 9290 plików”. Zlicza pliki i foldery rekurencyjnie, z wyłączeniem plików ukrytych, które można dodać za pomocą flagi-a
. Dla porównania, na moim komputerze zajęło mi to 4,8 sekundy, zanim drzewo policzyło cały mój katalog domowy, który zawierał 24777 katalogów, 238680 plików.find -type f | wc -l
zajęło to 5,3 sekundy, pół sekundy dłużej, więc myślę, że drzewo jest dość konkurencyjne pod względem szybkości.Dopóki nie masz żadnych podfolderów, drzewo jest szybkim i łatwym sposobem zliczania plików.
Ponadto, dla samej przyjemności, możesz używać
tree | grep '^├'
tylko do wyświetlania plików / folderów w bieżącym katalogu - jest to w zasadzie znacznie wolniejsza wersjals
.źródło
Brew install tail
dla OS X.tail
powinien być już zainstalowany w systemie Mac OS X.Szybka liczba plików w systemie Linux
Najszybsza liczba plików linux, jaką znam, to
Nie ma potrzeby wywoływania grep! Ale jak wspomniano, powinieneś mieć świeżą bazę danych (aktualizowaną codziennie przez zadanie cron lub ręcznie przez
sudo updatedb
).Od człowieka zlokalizuj
Dodatkowo powinieneś wiedzieć, że liczy również katalogi jako pliki!
BTW: Jeśli chcesz mieć przegląd plików i katalogów w Twoim systemie
Wyświetla liczbę katalogów, plików itp.
źródło
Pisząc to tutaj, ponieważ nie mam wystarczającej liczby punktów reputacji, aby skomentować odpowiedź, ale mogę zostawić własną odpowiedź, co nie ma sensu. Tak czy siak...
Jeśli chodzi o odpowiedź Christophera Schultza , proponuję zmienić stat na lstat i ewentualnie dodać sprawdzanie granic, aby uniknąć przepełnienia bufora:
Sugestią użycia lstat jest unikanie podążania za dowiązaniami symbolicznymi, które mogłyby prowadzić do cykli, jeśli katalog zawiera dowiązanie symboliczne do katalogu nadrzędnego.
źródło
lstat
było dobrą sugestią i zasługujesz na karmę za to. Ta sugestia została włączona do mojego kodu zamieszczonego powyżej, a teraz na GitHubie.Można spróbować, jeśli korzystają
opendir()
ireaddir()
wPerl
jest szybsze. Przykład tych funkcji znajdziesz tutajźródło
Ta odpowiedź jest szybsza niż prawie wszystko inne na tej stronie w przypadku bardzo dużych, bardzo zagnieżdżonych katalogów:
https://serverfault.com/a/691372/84703
locate -r '.' | grep -c "^$PWD"
źródło
locate -c -r '/path'
jak w roztworzePrzyszedłem tutaj, próbując policzyć pliki w zestawie danych zawierającym ~ 10K folderów po ~ 10K plików każdy. Problem z wieloma podejściami polega na tym, że niejawnie zapisują pliki 100M, co zajmuje wieki.
Pozwoliłem sobie rozszerzyć to podejście o Christophera-Schultza, tak aby obsługiwało przekazywanie katalogów przez args (jego podejście rekurencyjne również używa stat).
Umieść w pliku
dircnt_args.c
:Po a
gcc -o dircnt_args dircnt_args.c
możesz go wywołać w ten sposób:W przypadku plików 100M w folderach 10K powyższe czynności kończy się dość szybko (~ 5 minut przy pierwszym uruchomieniu, kontynuacja w pamięci podręcznej: ~ 23 s).
Tylko inne podejście, które ukończył w czasie krótszym niż godzina była ls z około 1 min na cache:
ls -f /your/dirs/* | wc -l
. Liczenie jest jednak zmniejszone o kilka nowych linii na katalog ...Poza oczekiwaniami, żadna z moich prób
find
zwróciła się w ciągu godziny: - /źródło
Najszybszym sposobem na Linuksie (pytanie jest oznaczone jako linux), jest użycie bezpośredniego wywołania systemowego. Oto mały program, który liczy pliki (tylko, bez katalogów) w katalogu. Możesz policzyć miliony plików i jest to około 2,5 razy szybsze niż „ls -f” i około 1,3–1,5 razy szybsze niż odpowiedź Christophera Schultza.
PS: Nie jest rekurencyjny, ale możesz go zmodyfikować, aby to osiągnąć.
źródło
opendir
/readdir
, ale podejrzewam, że na końcu sprowadza się to do prawie tego samego kodu. Wykonywanie wywołań systemowych w ten sposób również nie jest przenośne, a ponieważ ABI Linuksa nie jest stabilne, program skompilowany na jednym systemie nie gwarantuje prawidłowego działania na innym (chociaż dość dobrą radą jest kompilowanie czegokolwiek ze źródła na dowolnym systemie IMO * NIX ). Jeśli szybkość jest kluczowa, jest to dobre rozwiązanie, jeśli faktycznie poprawia szybkość - nie testowałem programów oddzielnie.ls
poświęca więcej czasu na sortowanie nazw plików, użycie-f
do wyłączenia sortowania pozwoli czasem zaoszczędzić:lub możesz użyć
find
:źródło
Zdałem sobie sprawę, że nieużywanie w przetwarzaniu pamięci, gdy masz ogromną ilość danych, jest szybsze niż „potokowanie” poleceń. Zapisałem więc wynik do pliku i po przeanalizowaniu
źródło
Powinieneś użyć "getdents" zamiast ls / find
Oto jeden bardzo dobry artykuł, który opisuje podejście getdents.
http://be-n.com/spw/you-can-list-a-million-files-in-a-directory-but-not-with-ls.html
Oto wyciąg:
ls i praktycznie każda inna metoda wyświetlania katalogu (w tym python os.listdir, find.) opierają się na libc readdir (). Jednak readdir () odczytuje tylko 32 KB wpisów w katalogu na raz, co oznacza, że jeśli masz dużo plików w tym samym katalogu (tj. 500 M wpisów katalogu), odczytanie wszystkich pozycji katalogu zajmie niesamowicie dużo czasu , zwłaszcza na wolnym dysku. W przypadku katalogów zawierających dużą liczbę plików musisz sięgnąć głębiej niż narzędzia, które opierają się na readdir (). Będziesz musiał użyć bezpośrednio metody getdents () syscall, zamiast metod pomocniczych z libc.
Możemy znaleźć kod C do wyświetlenia listy plików za pomocą getdents () stąd :
Aby szybko wyświetlić listę wszystkich plików w katalogu, musisz wykonać dwie modyfikacje.
Najpierw zwiększ rozmiar bufora z X do około 5 megabajtów.
Następnie zmodyfikuj główną pętlę, w której wypisuje informacje o każdym pliku w katalogu, aby pominąć wpisy z inode == 0. Zrobiłem to przez dodanie
W moim przypadku tak naprawdę zależało mi tylko na nazwach plików w katalogu, więc przepisałem również instrukcję printf (), aby wydrukować tylko nazwę pliku.
Skompiluj (nie potrzebuje żadnych zewnętrznych bibliotek, więc jest to bardzo proste)
Teraz po prostu biegnij
źródło
readdir()
rzeczywistości nie jest wolny. Potrzebuję solidnej sylwetki, zanim uznam, że warto odrzucić przenośność, aby uzyskać ten wzrost wydajności.Wolę następujące polecenie, aby śledzić zmiany w liczbie plików w katalogu.
Polecenie pozostawi otwarte okno, aby śledzić liczbę plików znajdujących się w katalogu z częstotliwością odświeżania 0,1 sekundy.
źródło
ls | wc -l
zakończy się to dla folderu z tysiącami lub milionami plików w 0,01 s? nawet twójls
jest niezwykle nieefektywny w porównaniu z innymi rozwiązaniami. A OP chce tylko policzyć, nie siedząc tam i patrząc na zmianę wyjściawatch
instrukcję po tym komentarzu i zobaczyłem, że 0,01 s (a nie 0,1 s) to liczba nierealna, ponieważ częstotliwość odświeżania większości ekranów komputerów osobistych wynosi tylko 60 Hz, a to w żaden sposób nie odpowiada na pytanie. OP zapytał o „Szybką liczbę plików Linuksa dla dużej liczby plików”. Nie przeczytałeś również żadnych dostępnych odpowiedzi przed wysłaniemPierwszych 10 reżyserów z największą liczbą plików.
źródło