find . -type f | wc -l 会统计当前目录下的所有文件(含子目录),并且不把目录本身算进去。真正有意思的通常是拆分结果——按扩展名、按子目录——而且有一种情况下 wc -l 会骗你:文件名里带换行。
1. 一共有多少文件
$ clai 这个文件夹和所有子文件夹里一共有多少文件→ find . -type f | wc -l20
-type f 排除了目录和符号链接,所以这个数字是有意义的。同一个目录下 ls | wc -l 会回答 3:它只看顶层,并且把目录也算作条目。
2. 按扩展名拆分
$ clai 按扩展名统计文件数量→ find . -type f -name '*.*' | sed 's/.*\.//' | sort | uniq -c | sort -rn 7 jpg 6 py 4 md 1 txt 1 png 1 bin
这通常才是问题背后的问题:这个目录到底由什么组成。-name '*.*' 会排除没有扩展名的文件;不加它,sed 会把整个文件名还给你。
3. 按子目录拆分
$ clai 统计每个子目录里的文件数→ for d in */; do printf '%5s %s\n' "$(find "$d" -type f | wc -l)" "$d"; done 3 docs/ 8 img/ 7 src/
对顶层目录做循环,在每个目录里递归计数。隐藏目录匹配不上 */,所以这里的总和小于全部数量。
4. 不算隐藏文件
$ clai 统计文件但跳过隐藏文件→ find . -type f -not -path '*/.*' | wc -l18
20 对 18,差的正好是那两个点开头的文件。-not -path '*/.*' 同时也排除了 .git 这类隐藏目录里的一切,而那通常正是你的本意。
5. 陷阱:文件名里的换行
$ clai 即使文件名里有换行也要可靠地统计文件数→ find . -type f -printf '.' | wc -c21
用一个名字横跨两行的文件验证过:同一目录下 find . -type f | wc -l 报的是 22。wc -l 数的是行,不是文件。-printf '.' 每个文件输出一个点,数字就又对了。不依赖 GNU 扩展的可移植写法是 find . -type f -print0 | tr -dc '\0' | wc -c。
6. 一行里同时给出文件数和目录数
$ clai 显示这里有多少文件、多少目录→ printf 'files: %s\ndirs: %s\n' "$(find . -type f | wc -l)" "$(find . -type d | wc -l)"
-type d 把当前目录也算了进去,所以目录数总比你以为的多一个。
Gotchas
ls | wc -l数的不是那回事。 它不进子目录,却把目录算进来,还跳过点开头的文件。回答你这个问题的是find -type f。wc -l会栽在名字里的换行上。 已验证:21 个文件,答案 22。如果目录来自外部——用户上传、解压出来、爬虫抓的——就用-printf '.'或-print0来数。find .会走进隐藏目录。 在仓库里,光是.git就能给总数添上几千个文件。用-not -path '*/.*'过滤,否则这个数字毫无意义。
Related questions
怎么只统计顶层? find . -maxdepth 1 -type f | wc -l 不会下到子目录。
怎么只统计某一类文件? find . -type f -name '*.log' | wc -l。大小写有区别,不想区分就用 -iname。
要的是体积而不是数量呢? du -sh . 给出总大小,du -h --max-depth=1 | sort -h 按子目录拆分。
See also
CliAI 会把"按类型统计有多少文件"变成完整的管道,并在执行前先显示出来。一行安装。