Jak policzyć pliki w każdym katalogu?

105

Jestem w stanie wyświetlić listę wszystkich katalogów według

find ./ -type d

Próbowałem wyświetlić zawartość każdego katalogu i policzyć liczbę plików w każdym katalogu, używając następującego polecenia

find ./ -type d | xargs ls -l | wc -l

Ale to zsumowało całkowitą liczbę wierszy zwróconych przez

find ./ -type d | xargs ls -l

Czy istnieje sposób, aby policzyć liczbę plików w każdym katalogu?

user784637
źródło
Szukasz sposobu, aby policzyć liczbę plików w każdym z podkatalogów bezpośrednio pod ./?
Tuxdude
5
Jak to jest pytanie niezwiązane z tematem? Chciałbym zobaczyć komentarze bliskich wyborców z uzasadnieniem! Jeśli to nie jest na temat, to do czego to należy? superużytkownik? Nie sądzę ...
InfantPro'Aravind '
6
skrypt powłoki, skrypt wsadowy są w zakresie programowania!
InfantPro'Aravind '
Miałem właśnie opublikować rozwiązanie Pythonic, ale zauważyłem, że pytanie jest zamknięte.
anatoly techtonik
głosował za ponownym otwarciem. Mogą istnieć inne odpowiedzi, które mogą być przydatne w wielu sytuacjach (w tym programowanie skryptowe, co jest powodem, dla którego dotarłem do tego pytania).
lepe

Odpowiedzi:

110

Zakładając, że masz GNU find, pozwól mu znaleźć katalogi, a bash zajmie się resztą:

find . -type d -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done
glenn jackman
źródło
2
Jest to tylko nieco inna wersja od powyższej, więc: (wskazówka: jest posortowana według nazwy i jest w csv) dla x in find . -maxdepth 1 -type d | sort; do y = find $x | wc -l; echo $ x, $ y; gotowe
pcarvalho
5
Świetny! Umieszczanie go w jednej linii (więc jest wygodny do bezpośredniego użycia w powłoce):find . -type d -print0 | while read -d '' -r dir; do files=("$dir"/*); printf "%5d files in directory %s\n" "${#files[@]}" "$dir"; done
lucaferrario
13
Musiałem uzyskać liczbę wszystkich plików (liczonych rekurencyjnie) w każdym podkatalogu. Ta modyfikacja zapewnia, że: find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find $dir -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
OmidS
1
@Kory Zrobią to:find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done | sort -rn -k1
OmidS
1
@OmidS Świetny oneliner, ale $dirw pierwszym komentarzu powinien znajdować się wewnątrz cudzysłowów, aby poprawnie obsługiwać nazwy katalogów ze spacjami . :find . -maxdepth 1 -type d -print0 | while read -d '' -r dir; do num=$(find "$dir" -ls | wc -l); printf "%5d files in directory %s\n" "$num" "$dir"; done
Radek Daniluk
183

Spowoduje to wyświetlenie liczby plików na katalog dla bieżącego poziomu katalogu:

du -a | cut -d/ -f2 | sort | uniq -c | sort -nr
Sebastian Piskorski
źródło
9
Zdecydowanie najlepsze (i najbardziej eleganckie) rozwiązanie, jeśli chce się rekurencyjnie wymienić liczbę plików w katalogach najwyższego poziomu.
itoctopus
13
Ma to dwa problemy: zlicza jeden plik na katalog więcej niż jest w rzeczywistości i daje bezużyteczną linię zawierającą rozmiar bieżącego katalogu jako „1 rozmiar ”. Oba można naprawić za pomocą du -a | sed '/.*\.\/.*\/.*/!d' | cut -d/ -f2 | sort | uniq -c. Dodaj, | sort -nraby posortować według liczby zamiast nazwy katalogu.
deser
3
Chciałbym zaznaczyć, że działa to również w OSX. (
Zwykłe
2
pobiera niepotrzebny rozmiar przez du -a. Lepszym sposobem jest użycie polecenia znajdź. ale główna idea jest dokładnie taka sama :)
Znik
5
odnaleźć . -typ f | wytnij -d / -f2 | sort | uniq -c | sort -nr # rozwiązuje problemy wymienione przez deser
jcomeau_ictx
28
find . -type f | cut -d/ -f2 | sort | uniq -c
  • find. -type f aby znaleźć wszystkie elementy pliku typu
  • cut -d/ -f2 aby wyciąć swój określony folder
  • sort aby posortować listę nazw folderów
  • uniq -c aby zwrócić, ile razy każda nazwa folderu została policzona
DCZ
źródło
8
Jest to o wiele lepsze niż zaakceptowana odpowiedź, ponieważ otrzymujesz podsumowanie katalogów najwyższego poziomu!
Jason Floyd
3
To powinna być akceptowana odpowiedź. Proste i zrozumiałe.
xssChauhan
1
Najlepszą odpowiedzią, jaką należy przyjąć, jest ta.
loretoparisi
1
Prosty, elegancki i idealny do moich potrzeb.
RichR
Idealny. Można go rozszerzyć tak, aby obejmował podkatalogi, zastępując specyfikatory pól listą specyfikatorów pól. Np .:find . -type f | cut -d/ -f2,3 | sort | uniq -c
glony
15

Możesz znaleźć wszystkie pliki, usunąć nazwy plików, pozostawiając linię zawierającą tylko nazwę katalogu dla każdego pliku, a następnie policzyć, ile razy pojawia się każdy katalog:

find . -type f |
sed 's%/[^/]*$%%' |
sort |
uniq -c

Jedynym problemem jest to, że masz jakiekolwiek nazwy plików lub nazwy katalogów zawierające znak nowej linii, co jest raczej mało prawdopodobne. Jeśli naprawdę musisz martwić się nowymi wierszami w nazwach plików lub nazwach katalogów, proponuję je znaleźć i naprawić, aby nie zawierały nowych linii (i po cichu przekonać winnego do błędu w ich postępowaniu).


Jeśli jesteś zainteresowany liczbą plików w każdym podkatalogu bieżącego katalogu, licząc wszystkie pliki w dowolnych podkatalogach wraz z plikami w bezpośrednim podkatalogu, dostosowałbym sedpolecenie tylko do drukowania katalog najwyższego poziomu:

find . -type f |
sed -e 's%^\(\./[^/]*/\).*$%\1%' -e 's%^\.\/[^/]*$%./%' |
sort |
uniq -c

Pierwszy wzór przechwytuje początek nazwy, kropkę, ukośnik, nazwę aż do następnego ukośnika i ukośnik i zastępuje wiersz tylko pierwszą częścią, więc:

./dir1/dir2/file1

jest zastąpiony przez

./dir1/

Drugie zastąpienie przechwytuje pliki bezpośrednio w bieżącym katalogu; nie mają ukośnika na końcu, a te są zastępowane przez ./. Sortowanie i liczenie działa wtedy tylko na liczbie nazw.

Jonathan Leffler
źródło
1
To nie wyświetla nazw katalogów, które nie zawierają żadnych plików. Nie jestem pewien, czy jest to wymagane.
Austin Phillips
To prawda, że ​​nie. Naprawienie tego nie jest szczególnie proste, ponieważ nie ma gwarancji, że puste nazwy katalogów pojawią się nawet w danych wyjściowych programu find. Niektórzy mogą: jeśli istnieje plik dir1/dir2/dir3/file1, ale dir1/dir2zawiera tylko podkatalogi (bez zwykłych plików), wtedy można wywnioskować o jego obecności. Ale jeśli dir1/dir4nie ma plików, nazwa po prostu się nie pojawia.
Jonathan Leffler
Bardzo przydatna odpowiedź, jeśli chcesz tylko zobaczyć podkatalogi bieżącego katalogu.
xixixao
Właśnie wpadłem, żeby podziękować. 3 lata po tym, jak to zostało opublikowane, chciałem policzyć foldery drugiego poziomu w każdym folderze. Twój post zaoszczędził mi potencjalnie wiele godzin majstrowania przy sedzie, znajdowaniu i nie wiadomo co jeszcze
Corvin
13

Oto jeden sposób, ale prawdopodobnie nie jest to najbardziej efektywny.

find -type d -print0 | xargs -0 -n1 bash -c 'echo -n "$1:"; ls -1 "$1" | wc -l' --

Daje takie dane wyjściowe, jak nazwa katalogu, po której następuje liczba wpisów w tym katalogu. Zwróć uwagę, że licznik wyjściowy będzie również zawierał pozycje katalogu, które mogą nie być tym, czego chcesz.

./c/fa/l:0
./a:4
./a/c:0
./a/a:1
./a/a/b:0
Austin Phillips
źródło
Wydaje się bardzo kosztowne, aby uruchomić 3 polecenia ( bash, ls, wc) dla każdego katalogu znaleziona przez find.
Jonathan Leffler
@JonathanLeffler Zgoda, stąd pierwsza linijka mojej odpowiedzi. Twoje rozwiązanie jest lepsze.
Austin Phillips
fajnie, tego szukam, czy mogę zapytać, co to jest „-” na końcu?
raz
1
@once The - należy do polecenia bash, które zostanie stworzone przez xargs. Od man bash, A -- signals the end of options and disables further option processing. W tym przypadku zapobiegnie to, że błędnie nazwany plik znaleziony jako część znalezienia nie stanie się częścią przetwarzania argumentów dla bash.
Austin Phillips,
8

Rozwiązanie wszystkich innych ma jedną lub drugą wadę.

find -type d -readable -exec sh -c 'printf "%s " "$1"; ls -1UA "$1" | wc -l' sh {} ';'

Wyjaśnienie:

  • -type d: interesują nas katalogi.
  • -readable: Chcemy ich tylko wtedy, gdy można wyświetlić listę plików, które się w nich znajdują. Zauważ, że findnadal będzie generować błąd, gdy spróbuje wyszukać w nich więcej katalogów, ale to zapobiega ich wywoływaniu -exec.
  • -exec sh -c BLAH sh {} ';': dla każdego katalogu uruchom ten fragment skryptu, $0ustawiając wartość shi $1ustawioną na nazwę pliku.
  • printf "%s " "$1": przenośnie i minimalnie wypisuje nazwę katalogu, po której następuje tylko spacja, a nie znak nowej linii.
  • ls -1UA: wyświetla pliki, po jednym w każdym wierszu, w kolejności katalogów (aby uniknąć blokowania potoku), wyłączając tylko specjalne katalogi .i..
  • wc -l: policz linie
o11c
źródło
1
Modyfikacja polegająca na wyświetlaniu plików jako pierwszych w wierszu i sortowaniu według nich:find -type d -readable -exec sh -c 'ls -1UA "$1" | wc -l | tr -d "\n" ; printf "\t%s\n" "$1" ' sh {} ';' | sort -n
Evgeni Sergeev
wykonuje powłokę wiele razy, a następnie jest powolna i mocno wykorzystuje zasoby.
Znik
6

Nieznacznie zmodyfikowana wersja odpowiedzi Sebastiana za pomocą findzamiast du(aby wykluczyć narzut związany z rozmiarem pliku, który dumusi działać i który nigdy nie jest używany):

 find ./ -mindepth 2 -type f | cut -d/ -f2 | sort | uniq -c | sort -nr

-mindepth 2parametr służy do wykluczania plików w bieżącym katalogu. Jeśli ją usuniesz, zobaczysz kilka linii, takich jak:

  234 dir1
  123 dir2
    1 file1
    1 file2
    1 file3
      ...
    1 fileN

(podobnie jak duwariant bazujący)

Jeśli musisz policzyć również pliki w bieżącym katalogu, użyj tej ulepszonej wersji:

{ find ./ -mindepth 2 -type f | cut -d/ -f2 | sort && find ./ -maxdepth 1 -type f | cut -d/ -f1; } | uniq -c | sort -nr

Dane wyjściowe będą wyglądać następująco:

  234 dir1
  123 dir2
   42 .
Yoory N.
źródło
5

Można to również zrobić za pomocą pętli nad ls zamiast find

for f in */; do echo "$f -> $(ls $f | wc -l)"; done

Wyjaśnienie:

for f in */; - pętla po wszystkich katalogach

do echo "$f -> - wydrukuj nazwę każdego katalogu

$(ls $f | wc -l) - wywołaj ls dla tego katalogu i policz linie

Sixhobbits
źródło
1
Nie działa to poprawnie, jeśli nazwy katalogów zawierają spacje.
Xylol
Spróbujfor f ./* ; do echo $f $(ls "$f" | wc -l); done
4ndt3s
3

Powinno to zwrócić nazwę katalogu, po której następuje liczba plików w katalogu.

findfiles() {
    echo "$1" $(find "$1" -maxdepth 1 -type f | wc -l)
}

export -f findfiles

find ./ -type d -exec bash -c 'findfiles "$0"' {} \;

Przykładowe dane wyjściowe:

./ 6
./foo 1
./foo/bar 2
./foo/bar/bazzz 0
./foo/bar/baz 4
./src 4

Jest export -fto wymagane, ponieważ -execargument findfunkcji bash nie pozwala na wykonanie funkcji bash, chyba że wywołasz ją jawnie i musisz jawnie wyeksportować funkcję zdefiniowaną w bieżącym zakresie do nowej powłoki.

Tuxdude
źródło
Wydaje się to nadmiernie skomplikowane. Wydaje mi się również, że podaje skumulowane liczby dla hierarchii katalogów, takie jak ./dir1/dir2/dir3(zliczanie plików w dir1i ich podkatalogach razem, zamiast liczenia plików dir1/dir2/dir3oddzielnie od tych w dir1/dir2i oba oddzielnie od tych w /dir1).
Jonathan Leffler
Zrozumiałem, że tego chciał autor. Jeśli tak nie jest, zgadzam się, że odpowiedź nie dotyczy pytania.
Tuxdude
1
@JonathanLeffler - OK, czytając pytanie jeszcze raz, zdałem sobie sprawę, że masz rację - odpowiednio zmodyfikowałem odpowiedź.
Tuxdude
2

Połączyłem odpowiedź @glenn jackman i odpowiedź @ pcarvalho (na liście komentarzy jest coś nie tak z odpowiedzią pcarvalho, ponieważ dodatkowa funkcja sterowania stylem znaku ' ' '' (lewy przycisk)).

Mój skrypt może zaakceptować ścieżkę jako rozszerzenie i posortować listę katalogów jako ls -l, a także może rozwiązać problem "miejsca w nazwie pliku" .

#!/bin/bash
OLD_IFS="$IFS"
IFS=$'\n'
for dir in $(find $1 -maxdepth 1 -type d | sort); 
do
    files=("$dir"/*)
    printf "%5d,%s\n" "${#files[@]}" "$dir"
done
FS="$OLD_IFS"

Moja pierwsza odpowiedź w stackoverflow i mam nadzieję, że może komuś pomóc ^ _ ^

opuszczanie
źródło
1

odnaleźć . -type f -printf '% h \ n' | sort | uniq -c

podaje na przykład:

  5 .
  4 ./aln
  5 ./aln/iq
  4 ./bs
  4 ./ft
  6 ./hot
Roztrzepany
źródło
0

Próbowałem z niektórymi innymi tutaj, ale skończyło się na tym, że podfoldery zostały uwzględnione w liczbie plików, gdy chciałem tylko pliki. Spowoduje to wydrukowanie ./folder/path<tab>nnnz liczbą plików, bez podfolderów, dla każdego podfolderu w bieżącym folderze.

for d in `find . -type d -print` 
do 
  echo -e "$d\t$(find $d -maxdepth 1 -type f -print | wc -l)"
done
sthames42
źródło
0

Łatwy sposób na rekurencyjne wyszukiwanie plików danego typu. W tym przypadku pliki .jpg dla wszystkich folderów w bieżącym katalogu:

find . -name *.jpg -print | wc -l

RexBarker
źródło
0

Super szybkie cudowne polecenie, które rekurencyjnie przechodzi przez pliki, aby policzyć liczbę obrazów w katalogu i uporządkować dane wyjściowe według rozszerzenia obrazu:

find . -type f | sed -e 's/.*\.//' | sort | uniq -c | sort -n | grep -Ei '(tiff|bmp|jpeg|jpg|png|gif)$'

Kredyty: https://unix.stackexchange.com/a/386135/354980

tsveti_iko
źródło
0

To może być inny sposób przeglądania struktur katalogów i dostarczania szczegółowych wyników.

find . -type d  | awk '{print "echo -n \""$0"  \";ls -l "$0" | grep -v total | wc -l" }' | sh 
Joseph Earnest
źródło
0

Edytowałem skrypt tak, aby wykluczyć wszystkie node_moduleskatalogi wewnątrz analizowanego.

Można to wykorzystać do sprawdzenia, czy liczba plików w projekcie przekracza maksymalną liczbę, którą może obsłużyć obserwator plików.

find . -type d ! -path "*node_modules*" -print0 | while read -d '' -r dir; do
    files=("$dir"/*)
    printf "%5d files in directory %s\n" "${#files[@]}" "$dir"
done

Aby sprawdzić maksymalną liczbę plików, które system może oglądać:

cat /proc/sys/fs/inotify/max_user_watches

node_modules folder powinien zostać dodany do ścieżek wykluczonych z IDE / edytora w powolnych systemach, a liczba innych plików nie powinna idealnie przekraczać maksimum (można to jednak zmienić).

Fundator
źródło
-1

To da ogólną liczbę.

for file in */; do echo "$file -> $(ls $file | wc -l)"; done | cut -d ' ' -f 3| py --ji -l 'numpy.sum(l)'
Naga Venkatesh Gavini
źródło
Nie, nie będzie. Uwzględnia tylko jeden poziom podkatalogów.
Kusalananda