为什么`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

有什么解释?

Answers:


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.

总数从1220使用时的,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
“如果文件名中包含新行” ... 不寒而栗
Petah

8
man ls您所知,您可以避免使用-b(转义)或-q(忽略它们)控制字符。因此对于计数而言,ls -1q | wc -l显示非隐藏文件是安全而准确的。ls -1qA | wc -l计算隐藏文件(但不包括...)。我正在使用-1而不是-l因为它应该更快。
奥利(Oli)

18

user4556274已经回答为什么。我的回答仅用于提供有关如何正确计数文件的其他信息。

在Unix社区中,普遍的共识是,解析文件的输出ls是一个非常非常糟糕的主意,因为文件名可以包含控制字符或隐藏字符。例如,由于文件名中有换行符,我们ls | wc -l告诉我们输出ls(确实有5行),但实际上目录中只有4个文件。

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

方法1:查找实用程序

find命令通常用于解析文件名,它可以通过打印inode号来帮助我们。无论是目录还是文件,它都只有一个唯一的索引节点号。因此,使用-printf "%i\n"和排除.via -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缺少数组的限制。可以在Unix&Linux上的Gille的答案中找到执行额外检查的版本。

在支持数组的shell中,例如bash,我们可以使用

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

正如钢铁司机在评论中提出的

find过去使用过的方法wc和globstar 相似的技巧可用于stat计算每行的索引节点数:

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

另一种方法是在for循环中使用通配符。(请注意,此测试使用不同的目录来测试此方法是否归入子目录,而子目录不会这样做-16是my中已验证的项目数~/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
在bash中,另一种选择是使用数组,例如 items=( dir/* ); echo ${#items[@]}(添加shopt -s dotglob以包括隐藏文件)。
steeldriver '16

1
通过打印inode编号,可以根据需要轻松过滤硬链接find | sort -u | wc -l
彼得·科德斯

@steeldriver:我认为bash-array方法不太可能更快。如果您希望它是递归的,则需要使用items=( dir/** )(with shopt -s globstar),但是bash不会利用readdir中的额外元数据,因此它会统计每个目录条目以查看它本身是否是目录。许多文件系统确实将文件类型存储在目录条目中,因此readdir可以在不访问索引节点的情况下将其返回。(例如,最新的非默认XFS拥有此功能,我认为ext4拥有更长的时间。)如果strace发现,您会发现与stat调用bash 相比,系统调用要少得多。
彼得·科德斯

2
为什么不只是使用print(len(os.listdir('.')))?要键入的字符较少,并且还避免访问带有双下划线的属性。
edwinksl

1
@edwinksl编辑,谢谢
Sergiy Kolodyazhnyy '16年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.