Tại sao `ls -l` đếm nhiều tệp hơn tôi?


25

Rõ ràng tôi không thể đếm được. Tôi nghĩ có ba tập tin trong/media

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

Tuy nhiên, ls -ltìm thấy 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

Và, nếu tôi làm ls -latôi chỉ nhận được ...ngoài những điều trên, nhưng số lượng làtotal 20

Lời giải thích là gì?

Câu trả lời:


33

Số lượng 12bạn nhìn thấy không phải là số lượng tệp, mà là số lượng khối đĩa được tiêu thụ.

Từ 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.

Tổng số đi từ 12đến 20khi bạn sử dụng ls -lathay ls -lvì bởi vì bạn đang đếm hai thư mục bổ sung: .... Bạn đang sử dụng bốn khối đĩa cho mỗi thư mục (trống), do đó tổng số của bạn tăng từ 3 × 4 đến 5 × 4. (Trong tất cả khả năng, bạn đang sử dụng một khối đĩa 4096 byte cho mỗi thư mục, như infotrang chỉ ra, tiện ích không kiểm tra định dạng đĩa, nhưng giả sử kích thước khối 1024trừ khi được hướng dẫn khác.)

Nếu bạn chỉ muốn lấy số lượng tệp, bạn có thể thử một cái gì đó như

ls | wc -l

13
ls | wc -lsẽ thất bại nếu có các tệp có dòng mới trong tên tệp. Điều này là kiên cường hơn:find . -mindepth 1 -maxdepth 1 -printf . | wc -c
Flimm

20
"nếu tên tệp có một dòng mới trong đó" ... rùng mình
Petah

8
Như man lssẽ cho bạn biết, bạn có thể tránh các ký tự điều khiển bằng -b(thoát chúng) hoặc -q(bỏ qua chúng). Vì vậy, để đếm, ls -1q | wc -lan toàn và chính xác để hiển thị các tệp không bị ẩn. ls -1qA | wc -lđể đếm các tập tin ẩn (nhưng không ...). Tôi đang sử dụng -1thay -lvì vì nó sẽ nhanh hơn.
Oli

18

user4556274 đã trả lời những lý do tại sao . Câu trả lời của tôi chỉ phục vụ để cung cấp thêm thông tin cho thế nào để đếm đúng file.

Trong cộng đồng Unix, sự đồng thuận chung là phân tích cú pháp đầu ra lslà một ý tưởng rất tồi , vì tên tệp có thể chứa các ký tự điều khiển hoặc ký tự ẩn. Ví dụ: do một ký tự dòng mới trong tên tệp, chúng tôi đã ls | wc -lcho chúng tôi biết có 5 dòng trong đầu ra của ls(mà nó có), nhưng thực tế chỉ có 4 tệp trong thư mục.

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

Phương pháp # 1: tìm tiện ích

Các findlệnh, mà thường được sử dụng để làm việc xung quanh phân tích cú pháp tên tập tin, có thể giúp chúng ta ở đây bằng cách in các số inode . Có thể là một thư mục hoặc một tập tin, nó chỉ có một số inode duy nhất. Do đó, sử dụng -printf "%i\n"và loại trừ .thông qua -not -name "."chúng tôi có thể có số lượng chính xác của các tệp. (Lưu ý việc sử dụng -maxdepth 1để ngăn đệ quy giảm dần vào các thư mục con)

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

Phương pháp # 2: sao

Cách đơn giản, nhanh chóng và chủ yếu là di động:

$ set -- * 
$ echo $#
228

setlệnh được sử dụng để đặt tham số vị trí của shell (các $<INTEGER>biến, như trong echo $1). Điều này thường được sử dụng để làm việc xung quanh việc /bin/shhạn chế thiếu mảng. Một phiên bản thực hiện kiểm tra bổ sung có thể được tìm thấy trong câu trả lời của Gille trên Unix & Linux.

Trong shell hỗ trợ mảng, chẳng hạn như bash, chúng ta có thể sử dụng

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

như đề xuất của Steeldo trong các ý kiến .

Thủ thuật tương tự với findphương thức được sử dụng wcvà globalstar có thể được sử dụng statđể đếm số lượng inode trên mỗi dòng:

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

Một cách tiếp cận khác là sử dụng ký tự đại diện trong forvòng lặp. (Lưu ý, kiểm tra này sử dụng một thư mục khác để kiểm tra xem phương pháp này có đi vào thư mục con hay không - 16 là số mục được xác minh trong mục của tôi ~/bin)

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

Phương pháp # 3: ngôn ngữ / thông dịch viên khác

Python cũng có thể xử lý các tên tệp có vấn đề thông qua việc in độ dài của danh sách được cung cấp cho os.listdir()hàm của tôi (không phải là đệ quy và sẽ chỉ liệt kê các mục trong thư mục được cung cấp dưới dạng đối số).

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

Xem thêm


2
Trong bash, một tùy chọn khác sẽ là sử dụng một mảng, ví dụ items=( dir/* ); echo ${#items[@]}(thêm shopt -s dotglobđể bao gồm các tệp ẩn).
Steeldo

1
In số inode giúp dễ dàng lọc các liên kết cứng nếu muốn, với find | sort -u | wc -l.
Peter Cordes

@steel ấn: Tôi nghĩ rằng phương pháp bash-Array khó có thể nhanh hơn. Nếu bạn muốn nó được đệ quy, bạn cần sử dụng items=( dir/** )(với shopt -s globstar), nhưng bash không tận dụng siêu dữ liệu bổ sung từ readdir, do đó, nó thống kê mọi mục nhập thư mục để xem liệu đó có phải là thư mục không. Nhiều hệ thống tập tin lưu trữ kiểu tệp trong mục nhập thư mục, vì vậy readdir có thể trả về nó mà không cần truy cập vào các nút. (ví dụ: XFS không mặc định mới nhất có cái này và tôi nghĩ rằng ext4 đã có nó lâu hơn.) Nếu bạn stracetìm thấy, bạn sẽ thấy statcác cuộc gọi hệ thống ít hơn nhiều so với bash.
Peter Cordes

2
Tại sao không chỉ sử dụng print(len(os.listdir('.')))? Ít ký tự để nhập và cũng tránh truy cập các thuộc tính gấp đôi.
edwinksl

1
@edwinksl đã được chỉnh sửa, thx
Sergiy Kolodyazhnyy
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.