CentOS 5.9
Pewnego dnia natknąłem się na problem, w którym katalog miał wiele plików. Aby to policzyć, pobiegłemls -l /foo/foo2/ | wc -l
Okazuje się, że w jednym katalogu znajdowało się ponad milion plików (długa historia - główna przyczyna została naprawiona).
Moje pytanie brzmi: czy istnieje szybszy sposób na policzenie? Jaki byłby najskuteczniejszy sposób na policzenie?
ls -l|wc -l
byłby wyłączony o jeden ze względu na całkowitą liczbę bloków w pierwszym wierszuls -l
wyniku-A
flagi.-l
jest również problematyczne z powodu odczytu metadanych pliku w celu wygenerowania rozszerzonego formatu listy. Wymuszanie NIE-l
przy użyciu\ls
jest znacznie lepszą opcją (-1
zakłada się przy przesyłaniu danych wyjściowych.) Zobacz najlepsze rozwiązanie tutaj, w odpowiedzi Gillesa .ls -l
nie wyświetla żadnych ukrytych plików ani wpisów.
i..
.ls -a
Wyjście zawiera ukryte pliki, w tym.
i..
jednocześniels -A
wyjście zawiera ukryte pliki bez.
i..
. W odpowiedzi Gillesadotglob
opcja powłoki bash powoduje, że rozszerzenie zawiera ukryte pliki z wyłączeniem.
i..
.Odpowiedzi:
Krótka odpowiedź:
(Obejmuje to,
.
a..
więc odejmij 2.)Gdy wyświetlasz listę plików w katalogu, mogą się zdarzyć trzy typowe rzeczy:
ls
polecenie.stat
celu pobrania metadanych dotyczących każdej pozycji katalogu, na przykład, czy jest to katalog.# 3 jest zdecydowanie najdroższym, ponieważ wymaga załadowania i-węzła dla każdego pliku. Dla porównania, wszystkie nazwy plików potrzebne dla nr 1 są przechowywane w zwięzłej formie w kilku blokach. # 2 marnuje trochę czasu procesora, ale często nie przeszkadza.
Jeśli w nazwach plików nie ma nowego wiersza, prosty
ls -A | wc -l
informuje, ile plików znajduje się w katalogu. Uważaj, że jeśli masz aliasls
, może to wywołać wywołaniestat
(np.ls --color
Lubls -F
znać typ pliku, który wymaga wywołaniastat
), a więc z linii poleceń, zadzwońcommand ls -A | wc -l
lub\ls -A | wc -l
unikaj aliasu.Jeśli w nazwie pliku znajdują się znaki nowej linii, to czy nowe linie są na liście, czy nie, zależy od wariantu Uniksa. Coreutils GNU i BusyBox domyślnie wyświetlają się
?
dla nowej linii, więc są bezpieczne.Zadzwoń,
ls -f
aby wyświetlić listę wpisów bez ich sortowania (# 2). To automatycznie się włącza-a
(przynajmniej w nowoczesnych systemach).-f
Opcja jest w POSIX ale ze statusem opcjonalnego; większość implementacji obsługuje to, ale nie BusyBox. Ta opcja-q
zastępuje znaki niedrukowalne, w tym znaki nowego wiersza, przez?
; jest POSIX, ale nie jest obsługiwany przez BusyBox, więc pomiń go, jeśli potrzebujesz obsługi BusyBox kosztem przeliczania plików, których nazwa zawiera znak nowej linii.Jeśli katalog nie ma podkatalogów, wówczas większość wersji
find
nie wywołastat
swoich wpisów (optymalizacja katalogu liści: katalog z liczbą linków 2 nie może mieć podkatalogów, więcfind
nie trzeba wyszukiwać metadanych wpisów, chyba że stan taki, jak tego-type
wymaga). Więcfind . | wc -l
jest przenośny, szybki sposób liczyć plików w katalogu, pod warunkiem, że katalog ma podkatalogi, a nie nazwa pliku zawiera znak nowej linii.Jeśli katalog nie ma podkatalogów, ale nazwy plików mogą zawierać znaki nowej linii, wypróbuj jeden z nich (drugi powinien być szybszy, jeśli jest obsługiwany, ale może nie być zauważalny).
Z drugiej strony nie używaj,
find
jeśli katalog ma podkatalogi: nawetfind . -maxdepth 1
wywołujestat
każdy wpis (przynajmniej z GNU find i BusyBox find). Unikasz sortowania (# 2), ale płacisz cenę za wyszukiwanie i-węzłów (# 3), które zabija wydajność.W powłoce bez zewnętrznych narzędzi można uruchomić zliczanie plików w bieżącym katalogu za pomocą
set -- *; echo $#
. Pomija to pliki kropkowe (pliki, których nazwa zaczyna się od.
) i zgłasza 1 zamiast 0 w pustym katalogu. Jest to najszybszy sposób na zliczanie plików w małych katalogach, ponieważ nie wymaga uruchomienia programu zewnętrznego, ale (oprócz zsh) marnuje czas na większe katalogi z powodu kroku sortowania (# 2).W bash jest to niezawodny sposób na policzenie plików w bieżącym katalogu:
W ksh93 jest to niezawodny sposób na zliczanie plików w bieżącym katalogu:
W zsh jest to niezawodny sposób na policzenie plików w bieżącym katalogu:
Jeśli masz
mark_dirs
zestaw opcji, należy ją wyłączyć:a=(*(DNoN^M))
.W dowolnej powłoce POSIX jest to niezawodny sposób na policzenie plików w bieżącym katalogu:
Wszystkie te metody sortują nazwy plików, z wyjątkiem jednej zsh.
źródło
find -maxdepth 1
łatwo dotrzymuje kroku, o\ls -U
ile nie dodasz czegoś takiego jak-type
deklaracja, która musi wykonać dalsze kontrole. Czy jesteś pewien, że GNU znajduje wywołaniastat
? Nawet spowolnieniefind -type
jest niczym w porównaniu z tym, ilels -l
torfowisk sprawi, że zwrócą szczegóły pliku. Z drugiej strony wyraźny zwycięzca prędkościzsh
korzysta z globu bez sortowania. (posortowane globusy są 2x wolniejsze niżls
podczas gdy nie sortujące globusy są 2x szybsze). Zastanawiam się, czy typy systemów plików znacząco wpłynęłyby na te wyniki.strace
. Jest to prawdą tylko wtedy, gdy katalog ma podkatalogi: w przeciwnym raziefind
rozpocznie się optymalizacja katalogu liści (nawet bez-maxdepth 1
), powinienem o tym wspomnieć. Na wynik może wpływać wiele rzeczy, w tym typ systemu plików (wywoływaniestat
jest znacznie droższe w systemach plików reprezentujących katalogi jako listy liniowe niż w systemach plików reprezentujących katalogi jako drzewa), niezależnie od tego, czy wszystkie i-węzły zostały utworzone razem, a zatem są blisko na dysku, zimnej lub gorącej pamięci podręcznej itp.ls -f
jest to niezawodny sposób zapobiegania dzwonieniustat
- często jest to dziś po prostu opisywane jako „wynik nie jest sortowany” (co również powoduje), i obejmuje.
i..
.-A
i-U
nie są standardowymi opcjami.\ls -afq *[0-9].pdb | wc -l
version sh (AT&T Research) 93u+ 2012-08-01
na moim systemie opartym na Debianie,FIGNORE
wydaje się nie działać. Te.
i..
wpisy są zawarte w otrzymanej tablicyJest znacznie szybszy na moim komputerze, ale
.
katalog lokalny jest dodawany do liczby.źródło
-type
parametru,find
powinno być szybsze niżls
-mindepth 1
aby pominąć sam katalog.ls -1U
zanim potok zużyje nieco mniej zasobów, ponieważ nie próbuje sortować wpisów plików, po prostu odczytuje je podczas sortowania w folderze na dysku. Daje również mniejszą wydajność, co oznacza nieco mniejszą pracęwc
.Możesz również użyć,
ls -f
który jest mniej więcej skrótemls -1aU
.Nie wiem jednak, czy istnieje efektywny pod względem zasobów sposób, aby to zrobić za pomocą polecenia bez przesyłania potokowego.
źródło
Kolejny punkt porównania. Ten program C, choć nie jest narzędziem powłoki, nie robi nic zbędnego. Zauważ, że ukryte pliki są ignorowane, aby dopasować wyjście
ls|wc -l
(ls -l|wc -l
jest wyłączone o jeden ze względu na całkowitą liczbę bloków w pierwszym wierszu wyniku).źródło
readdir()
interfejsu API stdio powoduje dodatkowe obciążenie i nie daje kontroli nad rozmiarem bufora przekazywanego do wywołania systemowego (getdents
w systemie Linux)Możesz spróbować
perl -e 'opendir($dh,".");$i=0;while(readdir $dh){$i++};print "$i\n";'
Byłoby interesujące porównać czasy z rurą pociskową.
źródło
find -maxdepth 1 | wc -l
,\ls -AU | wc -l
azsh
nie oparte na glob sortowania i liczenia tablicy). Innymi słowy, przewyższa opcje z różnymi nieefektywnościami, takimi jak sortowanie lub odczytywanie obcych właściwości pliku. Zaryzykowałbym stwierdzenie, ponieważ to też nic nie zarabia, nie warto używać prostszego rozwiązania, chyba że już jesteś w perlu :).
i..
pozycje katalogu w liczbie, więc musisz odjąć dwa, aby uzyskać rzeczywistą liczbę plików (i podkatalogów). W nowoczesnym Perluperl -E 'opendir $dh, "."; $i++ while readdir $dh; say $i - 2'
zrobiłby to.Na podstawie tej odpowiedzi mogę myśleć o tym jako o możliwym rozwiązaniu.
Skopiuj powyższy program C do katalogu, w którym pliki muszą być wymienione. Następnie wykonaj następujące polecenia:
źródło
ls -f
, wcale nie filtrujd_type
, po prostu włączd->d_ino != 0
; 3) odejmij 2 dla.
i..
.ls -f
.Rozwiązanie typu bash, nie wymagające żadnego zewnętrznego programu, ale nie wiem, jak wydajne:
źródło
Prawdopodobnie najbardziej zasobooszczędny sposób nie wymagałby zewnętrznych wywołań procesów. Więc postawiłbym na ...
źródło
Po naprawieniu problemu z odpowiedzi @Joela, gdzie został dodany
.
jako plik:find /foo/foo2 -maxdepth 1 | tail -n +2 | wc -l
tail
po prostu usuwa pierwszy wiersz, co oznacza, że.
nie jest już liczony.źródło
wc
wejściowej nie jest bardzo wydajne, ponieważ narzut zwiększa się liniowo względem wielkości wejściowej. W takim przypadku może po prostu zmniejszyć ostateczną liczbę, aby zrekompensować jej wyłączenie o jeden, co jest operacją o stałym czasie:echo $(( $(find /foo/foo2 -maxdepth 1 | wc -l) - 1))
let count = $(find /foo/foo2 -maxdepth 1 | wc -l) - 2
os.listdir () w python może wykonać pracę za Ciebie. Daje tablicę zawartości katalogu, z wyłączeniem specjalnego „.” i pliki „..”. Nie musisz też martwić się plikami abt ze znakami specjalnymi, takimi jak „\ n” w nazwie.
poniżej jest czas potrzebny na wykonanie powyższej komendy python w porównaniu z komendą „ls -Af”.
źródło
ls -1 | wc -l
przychodzi mi od razu do głowy. Czyls -1U
jest szybszy niżls -1
czysto akademicki - różnica powinna być znikoma, ale w przypadku bardzo dużych katalogów.źródło
Aby wykluczyć podkatalogi z liczby, oto odmiana zaakceptowanej odpowiedzi od Gillesa:
Zewnętrzna
$(( ))
interpretacja arytmetyczna odejmuje wynik drugiej$( )
podpowłoki od pierwszej$( )
. Pierwszy$( )
to dokładnie Gilles z góry. Drugi$( )
wyświetla liczbę katalogów „łączących” się z celem. Pochodzi zls -od
(wls -ld
razie potrzeby zamień), gdzie kolumna z liczbą twardych linków ma to szczególne znaczenie dla katalogów. „Link” count obejmuje.
,..
oraz wszystkie podkatalogi.Nie testowałem wydajności, ale wyglądałoby to podobnie. Dodaje statystykę katalogu docelowego i trochę narzutu dla dodanej podpowłoki i potoku.
źródło
Sądzę, że echo * byłoby bardziej wydajne niż jakiekolwiek polecenie „ls”:
źródło
echo 'Hello World'|wc -w
produkuje2
.