Dlaczego `ls -l` liczy więcej plików niż ja?

25

Najwyraźniej nie mogę liczyć. Myślę, że są trzy pliki/media

$ tree /media
/media
├── foo
├── onex
└── zanna
3 directories, 0 files

Jednak ls -lznajduje 12.

$ ls -l /media
total 12
drwxr-xr-x  2 root root 4096 Jul 31 20:57 foo
drwxrwxr-x  2 root root 4096 Jun 26 06:36 onex
drwxr-x---+ 2 root root 4096 Aug  7 21:17 zanna

A jeśli to zrobię ls -la, dostanę tylko .i ..oprócz powyższego, ale liczba jesttotal 20

Jakie jest wyjaśnienie?

Zanna
źródło

Odpowiedzi:

33

12Widać nie jest to liczba plików, ale liczba bloków dyskowych spożywane.

Od info coreutils 'ls invocation':

 For each directory that is listed, preface the files with a line
 `total BLOCKS', where BLOCKS is the total disk allocation for all
 files in that directory.  The block size currently defaults to 1024
 bytes, but this can be overridden (*note Block size::).  The
 BLOCKS computed counts each hard link separately; this is arguably
 a deficiency.

Całkowita idzie od 12celu 20, gdy używasz ls -lazamiast ls -lbo liczą dwa dodatkowe katalogi: .a ... Używasz czterech bloków dysku dla każdego (pustego) katalogu, więc twoja suma wynosi od 3 × 4 do 5 × 4. (Najprawdopodobniej używasz jednego bloku dysku o długości 4096 bajtów dla każdego katalogu; jak infowskazuje strona, narzędzie nie sprawdza formatu dysku, ale przyjmuje rozmiar bloku, o 1024ile nie podano inaczej.)

Jeśli chcesz po prostu uzyskać liczbę plików, możesz spróbować czegoś takiego

ls | wc -l
użytkownik4556274
źródło
13
ls | wc -lzakończy się niepowodzeniem, jeśli w nazwie pliku znajdują się pliki z nowym wierszem. Jest to bardziej odporne:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm
20
„jeśli w nazwach plików znajduje się nowa linia” ... wzdryga się
Petah,
8
Jak man lspowiedzą ci, możesz unikać znaków kontrolnych za pomocą -b(ucieka je) lub -q(pomija je). Więc do zliczania, ls -1q | wc -ljest bezpieczny i dokładny do pokazywania nie ukrytych plików. ls -1qA | wc -lliczyć ukryte pliki (ale nie .i ..). Używam -1zamiast, -lponieważ powinno to być szybsze.
Oli
18

użytkownik4556274 już odpowiedział na pytanie dlaczego . Moja odpowiedź służy jedynie do dostarczenia dodatkowych informacji na temat prawidłowego liczenia plików.

W społeczności Uniksa ogólny konsensus jest taki, że analiza wyniku lsjest bardzo, bardzo złym pomysłem , ponieważ nazwy plików mogą zawierać znaki kontrolne lub znaki ukryte. Na przykład, ze względu na znak nowej linii w nazwie pliku, ls | wc -lpowiedzieliśmy, że jest 5 wierszy na wyjściu ls(które ma), ale w rzeczywistości w katalogu są tylko 4 pliki.

$> touch  FILE$'\n'NAME                                                       
$> ls                                                                         
file1.txt  file2.txt  file3.txt  FILE?NAME
$> ls | wc -l
5

Metoda nr 1: Znajdź narzędzie

findPoleceń, który jest zwykle używany do pracy około analizowania nazw plików, może pomóc nam tutaj przez wydrukowanie numeru węzła . Czy to katalog, czy plik, ma tylko jeden unikalny numer i-węzła. Tak więc, używając -printf "%i\n"i wykluczając .za pośrednictwem -not -name ".", możemy mieć dokładną liczbę plików. (Zwróć uwagę na użycie, -maxdepth 1aby zapobiec rekurencyjnemu schodzeniu do podkatalogów)

$> find  -maxdepth 1 -not -name "." -print                                    
./file2.txt
./file1.txt
./FILE?NAME
./file3.txt
$> find  -maxdepth 1 -not -name "." -printf "%i\n" | wc -l                    
4

Metoda nr 2: globstar

Prosty, szybki i najczęściej przenośny sposób:

$ set -- * 
$ echo $#
228

setpolecenie służy do ustawiania parametrów pozycyjnych powłoki ( $<INTEGER>zmienne, jak w echo $1). Jest to często używane do obejścia /bin/shograniczenia brakujących tablic. Wersję, która wykonuje dodatkowe kontrole, można znaleźć w odpowiedzi Gille na Unix i Linux.

W powłokach obsługujących tablice, takich jak bash, możemy użyć

items=( dir/* )
echo ${#items[@]}

zgodnie z propozycją steeldriver w komentarzach .

Podobna findmetoda jak w przypadku metody wci globstar może być użyta statdo zliczania liczb i-węzłów w linii:

$> LC_ALL=C stat ./* --printf "%i\n" | wc -l                                          
4

Alternatywnym podejściem jest użycie symbolu wieloznacznego w forpętli. (Uwaga: ten test używa innego katalogu do sprawdzenia, czy to podejście schodzi do podkatalogów, a tak nie jest - 16 to zweryfikowana liczba elementów w moim ~/bin)

$> count=0; for item in ~/bin/* ; do count=$(($count+1)) ; echo $count ; done | tail -n 1                                
16

Metoda nr 3: inne języki / tłumacze

Python może również radzić sobie z problematycznymi nazwami plików, drukując długość listy podanej przez moją os.listdir()funkcję (która nie jest rekurencyjna i wyświetla tylko pozycje w katalogu podanym jako argument).

$> python -c "import os ; print os.listdir('.')"                              
['file2.txt', 'file1.txt', 'FILE\nNAME', 'file3.txt']
$>  python -c "import os ; print(len(os.listdir('.')))"                    
4

Zobacz też

Sergiy Kolodyazhnyy
źródło
2
W bash inną opcją byłoby użycie tablicy np. items=( dir/* ); echo ${#items[@]}(Dodanie w shopt -s dotglobcelu włączenia ukrytych plików).
steeldriver,
1
Drukowanie numerów i-węzłów ułatwia filtrowanie linków w razie potrzeby za pomocą find | sort -u | wc -l.
Peter Cordes,
@steeldriver: Myślę, że metoda bash-tablic raczej nie będzie szybsza. Jeśli chcesz, aby był rekurencyjny, musisz użyć items=( dir/** )(z shopt -s globstar), ale bash nie korzysta z dodatkowych metadanych z readdir, więc statystyki każdego wpisu katalogu, aby zobaczyć, czy jest to sam katalog. Wiele systemów plików przechowuje typ pliku we wpisie katalogu, więc readdir może go zwrócić bez dostępu do i-węzłów. (np. najnowszy nie-domyślny XFS ma to i myślę, że ext4 ma to dłużej.) Jeśli straceznajdziesz, zobaczysz o wiele mniej statwywołań systemowych niż strash bash.
Peter Cordes,
2
Dlaczego nie po prostu użyć print(len(os.listdir('.')))? Mniej znaków do wpisania, a także unika dostępu do podwójnie podkreślonych atrybutów.
edwinksl,
1
@edwinksl edytowane, dzięki
Sergiy Kolodyazhnyy