简短答案:
\ls -afq | wc -l
(这包括.
和..
,因此减去2。)
当您在目录中列出文件时,可能会发生三种常见情况:
- 枚举目录中的文件名。这是不可避免的:如果不枚举目录,就无法计数目录中的文件。
- 排序文件名。Shell通配符和
ls
命令可以执行此操作。
- 调用
stat
以检索有关每个目录条目的元数据,例如是否为目录。
#3是迄今为止最昂贵的,因为它需要为每个文件加载一个索引节点。相比之下,#1所需的所有文件名都紧凑地存储在几个块中。#2浪费了一些CPU时间,但通常不会破坏交易。
如果文件名中没有换行符,则简单ls -A | wc -l
告诉您目录中有多少个文件。请注意,如果您具有的别名ls
,这可能会触发对的调用stat
(例如,ls --color
或者ls -F
需要知道文件类型,这需要对进行调用stat
),因此请从命令行调用command ls -A | wc -l
或\ls -A | wc -l
避免使用别名。
如果文件名中包含换行符,是否列出换行符取决于Unix变体。GNU coreutils和BusyBox默认显示?
为换行符,因此很安全。
调用ls -f
以列出条目而不对它们进行排序(#2)。这将自动打开-a
(至少在现代系统上)。该-f
选项位于POSIX中,但具有可选状态。大多数实现都支持它,但BusyBox不支持。该选项-q
用?
;替换包括换行符在内的不可打印字符;它是POSIX,但不被BusyBox支持,因此如果您需要BusyBox支持,则可以忽略它,而要过多计算名称中包含换行符的文件。
如果目录没有子目录,则大多数版本find
都不会调用stat
其条目(叶目录优化:链接数为2的目录不能有子目录,因此find
不需要查找条目的元数据,除非条件,如-type
要求)。find . | wc -l
如果目录中没有子目录并且文件名中不包含换行符,那么这是一种可移植的快速计数目录中文件的方法。
如果该目录没有子目录,但是文件名可能包含换行符,请尝试其中之一(如果受支持,则第二个换行符应该较快,但可能并不明显)。
find -print0 | tr -dc \\0 | wc -c
find -printf a | wc -c
另一方面,find
如果目录具有子目录,则不要使用:甚至在每个条目上都find . -maxdepth 1
调用stat
(至少使用GNU find和BusyBox find)。您可以避免排序(#2),但要付出牺牲索引性能的代价(#3)。
在没有外部工具的Shell中,您可以使用来运行当前目录中的文件计数set -- *; echo $#
。这会遗漏点文件(名称以开头的文件.
),并在空目录中报告1而不是0。这是对小目录中文件进行计数的最快方法,因为它不需要启动外部程序,但是(由于zsh除外)由于排序步骤(#2),浪费了较大目录的时间。
在bash中,这是一种对当前目录中的文件进行计数的可靠方法:
shopt -s dotglob nullglob
a=(*)
echo ${#a[@]}
在ksh93中,这是一种对当前目录中的文件进行计数的可靠方法:
FIGNORE='@(.|..)'
a=(~(N)*)
echo ${#a[@]}
在zsh中,这是一种对当前目录中的文件进行计数的可靠方法:
a=(*(DNoN))
echo $#a
如果已设置mark_dirs
选项,请确保将其关闭:a=(*(DNoN^M))
。
在任何POSIX Shell中,这都是一种对当前目录中的文件进行计数的可靠方法:
total=0
set -- *
if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi
set -- .[!.]*
if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi
set -- ..?*
if [ $# -ne 1 ] || [ -e "$1" ] || [ -L "$1" ]; then total=$((total+$#)); fi
echo "$total"
除了zsh以外,所有这些方法都对文件名进行排序。
ls -l|wc -l
由于ls -l
输出的第一行中的总块数,该值将减少一个