Mengapa `ls-l` menghitung lebih banyak file daripada saya?

25

Sepertinya saya tidak bisa menghitung. Saya pikir ada tiga file dalam/media

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

Namun, ls -lditemukan 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

Dan, jika saya lakukan, ls -lasaya hanya mendapatkan .dan ..di samping di atas, tetapi hitungannya adalahtotal 20

Apa penjelasannya?

Zanna
sumber

Jawaban:

33

Yang 12Anda lihat bukan jumlah file, tetapi jumlah blok disk yang dikonsumsi.

Dari 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.

Total pergi dari 12ke 20ketika Anda menggunakan ls -labukan ls -lkarena Anda menghitung dua direktori tambahan: .dan ... Anda menggunakan empat blok disk untuk setiap direktori (kosong), sehingga total Anda berubah dari 3 × 4 hingga 5 × 4. (Dalam semua kemungkinan, Anda menggunakan satu blok disk 4096 byte untuk setiap direktori; seperti yang infoditunjukkan halaman, utilitas tidak memeriksa format disk, tetapi mengasumsikan ukuran blok 1024kecuali diperintahkan sebaliknya.)

Jika Anda hanya ingin mendapatkan jumlah file, Anda dapat mencoba sesuatu seperti

ls | wc -l
pengguna4556274
sumber
13
ls | wc -lakan gagal jika ada file dengan baris baru di nama file. Ini lebih tangguh:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm
20
"jika nama file memiliki baris baru di dalamnya" ... shudder
Petah
8
Seperti yang man lsakan memberitahu Anda, Anda dapat menghindari mengontrol karakter dengan -b(lolos dari mereka) atau -q(menghilangkannya). Jadi untuk penghitungan, ls -1q | wc -laman dan akurat untuk menampilkan file yang tidak tersembunyi. ls -1qA | wc -luntuk menghitung file yang tersembunyi (tetapi tidak .dan ..). Saya menggunakan -1bukan -lkarena itu harus lebih cepat.
Oli
18

user4556274 telah menjawab itu sebabnya . Jawaban saya hanya berfungsi untuk memberikan informasi tambahan tentang cara menghitung file dengan benar.

Dalam komunitas Unix, konsensus umum adalah bahwa mengurai output lsadalah ide yang sangat sangat buruk , karena nama file dapat berisi karakter kontrol atau karakter tersembunyi. Sebagai contoh, karena karakter baris baru dalam nama file, kami telah ls | wc -lmemberi tahu kami ada 5 baris dalam output ls(yang memang ada), tetapi dalam kenyataannya hanya ada 4 file dalam direktori.

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

Metode # 1: temukan utilitas

The findperintah, yang biasanya digunakan untuk bekerja di sekitar parsing nama file, dapat membantu kita di sini dengan mencetak nomor inode . Baik itu direktori atau file, itu hanya memiliki satu nomor inode unik. Dengan demikian, menggunakan -printf "%i\n"dan mengecualikan .melalui -not -name "."kami dapat memiliki jumlah file yang akurat. (Perhatikan penggunaan -maxdepth 1untuk mencegah turun secara rekursif ke dalam subdirektori)

$> 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

Metode # 2: globstar

Cara sederhana, cepat, dan sebagian besar portabel:

$ set -- * 
$ echo $#
228

setperintah digunakan untuk mengatur parameter posisi shell ( $<INTEGER>variabel, seperti pada echo $1). Ini sering digunakan untuk mengatasi /bin/shketerbatasan array yang kurang. Versi yang melakukan pemeriksaan tambahan dapat ditemukan dalam jawaban Gille di Unix & Linux.

Dalam shell yang mendukung array, seperti bash, kita dapat menggunakan

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

seperti yang diusulkan oleh steeldriver dalam komentar .

Trik serupa dengan findmetode yang digunakan wcdan globstar dapat digunakan statuntuk menghitung nomor inode per baris:

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

Pendekatan alternatif adalah dengan menggunakan wildcard di forloop. (Catatan, tes ini menggunakan direktori yang berbeda untuk menguji apakah pendekatan ini turun ke subdirektori, yang tidak - 16 adalah jumlah item yang diverifikasi di saya ~/bin)

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

Metode # 3: bahasa / penerjemah lain

Python juga dapat menangani nama file yang bermasalah melalui pencetakan panjang daftar yang diberikan os.listdir()fungsi saya (yang non-rekursif, dan hanya akan mendaftar item dalam direktori yang diberikan sebagai argumen).

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

Lihat juga

Sergiy Kolodyazhnyy
sumber
2
Dalam bash, opsi lain adalah menggunakan array misalnya items=( dir/* ); echo ${#items[@]}(menambahkan shopt -s dotglobuntuk menyertakan file tersembunyi).
steeldriver
1
Mencetak nomor inode memudahkan untuk memfilter hardlink jika diinginkan, dengan find | sort -u | wc -l.
Peter Cordes
@steeldriver: Saya pikir metode bash-array tidak mungkin lebih cepat. Jika Anda ingin rekursif, Anda perlu menggunakan items=( dir/** )(with shopt -s globstar), tetapi bash tidak memanfaatkan metadata tambahan dari readdir, jadi buat statistik setiap entri direktori untuk melihat apakah itu adalah direktori itu sendiri. Banyak sistem file yang menyimpan filetype dalam entri direktori, sehingga readdir dapat mengembalikannya tanpa mengakses inode. (mis. XFS non-default terbaru memiliki ini, dan saya pikir ext4 telah memilikinya lebih lama.) Jika Anda stracemenemukan, Anda akan melihat lebih sedikit statpanggilan sistem daripada mengganti bash.
Peter Cordes
2
Kenapa tidak pakai saja print(len(os.listdir('.')))? Lebih sedikit karakter untuk diketik dan juga menghindari mengakses atribut bergaris bawah dua kali lipat.
edwinksl
1
@edwinksl diedit, thx
Sergiy Kolodyazhnyy