ทำไม `ls -l` นับไฟล์มากกว่าฉัน


25

เห็นได้ชัดว่าฉันไม่สามารถนับได้ ฉันคิดว่ามีสามไฟล์ค่ะ/media

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

อย่างไรก็ตามls -lพบ 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

และถ้าฉันทำls -laฉันจะได้รับเท่านั้น.และ..นอกเหนือจากข้างต้น แต่การนับเป็นtotal 20

คำอธิบายคืออะไร

คำตอบ:


33

ที่12คุณเห็นไม่ใช่จำนวนไฟล์ แต่จำนวนดิสก์บล็อกที่ใช้ไป

จาก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.

รวมไปจาก12ไป20เมื่อคุณใช้ls -laแทนls -lเพราะคุณกำลังนับสองไดเรกทอรีเพิ่มเติม: และ. ..คุณกำลังใช้บล็อกดิสก์สี่บล็อกสำหรับแต่ละไดเรกทอรี (ว่าง) ดังนั้นผลรวมของคุณจะอยู่ระหว่าง 3 × 4 ถึง 5 × 4 (ในทุกโอกาสคุณกำลังใช้ดิสก์บล็อกหนึ่งบล็อกที่มีขนาด 4096 ไบต์สำหรับแต่ละไดเรกทอรีตามที่infoหน้าระบุไว้ ยูทิลิตี้ไม่ได้ตรวจสอบรูปแบบดิสก์ แต่จะถือว่าขนาดบล็อก1024ยกเว้นจะได้รับคำแนะนำเป็นอย่างอื่น)

หากคุณต้องการเพียงแค่รับจำนวนไฟล์คุณอาจลองทำสิ่งที่ต้องการ

ls | wc -l

13
ls | wc -lจะล้มเหลวหากมีไฟล์ที่มีการขึ้นบรรทัดใหม่ในชื่อไฟล์ นี่คือความยืดหยุ่นที่มากขึ้น:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm

20
"ถ้าชื่อไฟล์มีบรรทัดใหม่ในนั้น" ... ตัวสั่น
เปตรา

8
ตามที่man lsจะบอกคุณคุณสามารถหลีกเลี่ยงตัวอักษรควบคุมด้วย-b(หนีออกมา) หรือ-q(ละเว้น) ดังนั้นสำหรับการนับls -1q | wc -lมีความปลอดภัยและถูกต้องสำหรับการแสดงไฟล์ที่ไม่ซ่อน ls -1qA | wc -lเพื่อนับไฟล์ที่ซ่อน (แต่ไม่ใช่.และ..) ฉันใช้-1แทน-lเพราะมันน่าจะเร็วกว่า
Oli

18

user4556274 มีคำตอบอยู่แล้วทำไม คำตอบของฉันทำหน้าที่เพียงเพื่อให้ข้อมูลเพิ่มเติมสำหรับวิธีการนับไฟล์อย่างถูกต้อง

ในชุมชน Unix ความเห็นทั่วไปคือการแยกวิเคราะห์ผลลัพธ์lsเป็นความคิดที่เลวมากเนื่องจากชื่อไฟล์สามารถมีอักขระควบคุมหรืออักขระที่ซ่อนอยู่ ตัวอย่างเช่นเนื่องจากอักขระขึ้นบรรทัดใหม่ในชื่อไฟล์เราได้ls | wc -lบอกเราว่ามี 5 บรรทัดในผลลัพธ์ของls(ซึ่งมี) แต่ในความเป็นจริงมีเพียง 4 ไฟล์ในไดเรกทอรี

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

วิธีที่ # 1: ค้นหาโปรแกรมอรรถประโยชน์

findคำสั่งซึ่งโดยปกติจะใช้สำหรับการทำงานรอบแยกชื่อไฟล์ที่สามารถช่วยเราได้ที่นี่โดยการพิมพ์หมายเลขไอโหนด ไม่ว่าจะเป็นไดเรกทอรีหรือไฟล์มีเพียงหมายเลขไอโหนดที่ไม่ซ้ำกันเท่านั้น ดังนั้นการใช้-printf "%i\n"และการยกเว้น.ผ่านทาง-not -name "."เราสามารถมีจำนวนไฟล์ที่ถูกต้อง (โปรดทราบว่าการใช้-maxdepth 1เพื่อป้องกันไม่ให้ซ้ำไปมาในไดเรกทอรีย่อย)

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

วิธีที่ # 2: globstar

วิธีที่ง่ายรวดเร็วและพกพาได้ส่วนใหญ่:

$ set -- * 
$ echo $#
228

setคำสั่งจะใช้ในการตั้งค่าพารามิเตอร์ตำแหน่งของเปลือก ( $<INTEGER>ตัวแปรเช่นในecho $1) มักใช้เพื่อแก้ไข/bin/shข้อ จำกัด ของอาร์เรย์ที่ขาด รุ่นที่มีการตรวจสอบพิเศษสามารถพบได้ในคำตอบของ Gilleบน Unix & Linux

ในเชลล์ที่สนับสนุนอาร์เรย์เช่นbashเราสามารถใช้

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

ที่เสนอโดยsteeldriver ในการแสดงความคิดเห็น

เทคนิคคล้ายกับfindวิธีที่ใช้wcและ globstar สามารถใช้statเพื่อนับจำนวนไอโหนดต่อบรรทัด:

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

อีกทางเลือกหนึ่งคือการใช้ตัวแทนในforวง (หมายเหตุการทดสอบนี้ใช้ไดเรกทอรีที่แตกต่างกันเพื่อทดสอบว่าวิธีการนี้เข้าสู่ไดเรกทอรีย่อยหรือไม่ซึ่งไม่ใช่ - 16 คือจำนวนรายการที่ตรวจสอบแล้วในฉัน~/bin)

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

วิธีที่ # 3: ภาษา / ล่ามอื่น ๆ

Python สามารถจัดการกับชื่อไฟล์ที่มีปัญหาผ่านการพิมพ์ความยาวของรายการที่กำหนดให้os.listdir()ฟังก์ชั่นของฉัน(ซึ่งไม่เรียกซ้ำและจะแสดงรายการในไดเรกทอรีที่กำหนดเป็นอาร์กิวเมนต์เท่านั้น)

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

ดูสิ่งนี้ด้วย


2
ในทุบตีตัวเลือกอื่นจะใช้อาร์เรย์เช่น items=( dir/* ); echo ${#items[@]}(เพิ่มshopt -s dotglobเพื่อรวมไฟล์ที่ซ่อนอยู่)
ขับเหล็ก

1
พิมพ์หมายเลขไอโหนดทำให้ง่ายต่อการ hardlinks find | sort -u | wc -lกรองถ้าต้องการด้วย
Peter Cordes

@steeldriver: ฉันคิดว่าวิธี bash-array นั้นไม่น่าจะเร็วกว่านี้ ถ้าคุณต้องการที่จะเรียกซ้ำคุณต้องใช้items=( dir/** )(พร้อมshopt -s globstar) แต่ทุบตีไม่ได้ใช้ประโยชน์จากเมตาดาต้าเพิ่มเติมจาก readdir ดังนั้นมันจะสถิติทุกรายการไดเรกทอรีเพื่อดูว่ามันเป็นไดเรกทอรีตัวเอง ระบบไฟล์จำนวนมากเก็บไฟล์ประเภทไว้ในรายการไดเร็กทอรีดังนั้น readdir สามารถส่งคืนได้โดยไม่ต้องเข้าถึง inodes (เช่น XFS ที่ไม่ใช่ค่าเริ่มต้นล่าสุดมีสิ่งนี้และฉันคิดว่า ext4 ใช้งานได้นานกว่า) หากคุณstraceพบคุณจะเห็นการstatเรียกใช้ระบบน้อยลงกว่าการบีบอัดสเตรช
Peter Cordes

2
ทำไมไม่ใช้เพียงprint(len(os.listdir('.')))? จำกัด จำนวนอักขระที่จะพิมพ์และหลีกเลี่ยงการเข้าถึงแอตทริบิวต์ที่มีการขีดเส้นใต้เป็นสองเท่า
edwinksl

1
@edwinksl แก้ไขแล้วขอบคุณ
Sergiy Kolodyazhnyy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.