是否有一个bash命令来计算文件?

Is there a bash command which counts files?

是否有一个bash命令来计算与模式匹配的文件数?

例如,我想得到一个与此模式匹配的目录中所有文件的计数:log*


这个简单的一行程序应该在任何shell中工作,而不仅仅是bash:

1
ls -1q log* | wc -l

ls-1q为每个文件提供一行,即使它们包含空格或换行符等特殊字符。

输出通过管道传输到wc-l,它计算行数。


您可以使用bash安全地执行此操作(即不会被带有空格的文件或名称为
的文件窃听):

1
2
3
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}

如果没有匹配的文件,您需要启用nullglob,这样就不会在$logfiles数组中得到文本*.log。(请参见如何"撤消"一个"set-x"?例如如何安全重置。)


这里有很多答案,但有些没有考虑到

  • 包含空格、换行符或控制字符的文件名
  • 以连字符开头的文件名(假设一个名为-l的文件)
  • 隐藏文件,以点开头(如果glob是*.log,而不是log*
  • 与全局匹配的目录(例如,名为logs的目录与log*匹配)
  • 空目录(即结果为0)
  • 非常大的目录(列出所有目录可能会耗尽内存)

这里有一个解决方案可以处理所有这些问题:

1
ls 2>/dev/null -Ubad1 -- log* | wc -l

说明:

  • -U导致ls无法对条目进行排序,这意味着它不需要在内存中加载整个目录列表。
  • -b打印非图形字符的C样式转义,关键是使换行符打印为
  • -a打印出所有文件,甚至隐藏文件(当globallog*表示没有隐藏文件时,不严格需要)
  • -d打印目录而不试图列出目录的内容,这是ls通常会做的。
  • -1确保它在一列上(ls在写入管道时自动执行此操作,因此没有严格必要)
  • 2>/dev/null重定向stderr,以便如果有0个日志文件,忽略错误消息。(注意,shopt -s nullglob会导致ls列出整个工作目录。)
  • wc -l在生成目录列表时使用目录列表,因此ls的输出在任何时间点都不在内存中。
  • --文件名与使用--的命令分开,以便不被理解为ls的参数(如果log*被删除)。

shell会将log*扩展到文件的完整列表,如果文件太多,可能会耗尽内存,因此通过grep运行会更好:

1
ls -Uba1 | grep ^log | wc -l

最后一个处理非常大的文件目录,而不使用大量内存(尽管它确实使用了子shell)。-d不再是必需的,因为它只列出当前目录的内容。


对于递归搜索:

1
find . -type f -name '*.log' | wc -l

wc -w计算输出中的字数(bash将把*.log扩展为与该模式匹配的文件的空间分隔列表),而wc -l计算行数(find每行打印一个结果)。

对于非递归搜索,请执行以下操作:

1
find . -maxdepth 1 -type f -name '*.log' | wc -l


这个问题被接受的答案是错误的,但我的代表性很低,所以无法对其添加评论。

该问题的正确答案由Mat给出:

1
2
3
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}

接受的答案的问题是wc-l计算换行字符的数量,并对它们进行计数,即使它们以'?'的形式打印到终端。在"ls-l"的输出中。这意味着当文件名包含换行符时,接受的回答将失败。我已经测试了建议的命令:

1
ls -l log* | wc -l

它错误地报告值2,即使只有1个文件与模式匹配,而模式的名称恰好包含换行符。例如:

1
2
3
touch log$'
'
def
ls log* -l | wc -l

如果您有很多文件,并且不想使用优雅的shopt -s nullglob和bash数组解决方案,那么只要不打印出文件名(可能包含新行),就可以使用find等等。

1
2
find -maxdepth 1 -name"log*" -not -name".*" -printf '%i
'
| wc -l

这将找到所有与log*匹配的文件,并且不以.*开头-"not name.*"是reducant,但必须注意,"ls"的默认值是不显示点文件,但find的默认值是包含这些文件。

这是一个正确的答案,可以处理任何类型的文件名,因为文件名不会在命令之间传递。

但是,shopt nullglob的答案是最好的答案!


这是我的单程机票。

1
 file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)

您可以使用-r选项查找文件以及递归目录中的文件。

1
2
3
ls -R | wc -l // to find all the files

ls -R | grep php | wc -l // to find the files which contains the word php

你可以在grep上使用模式


我总是这样做:

ls log* | awk 'END{print NR}'


1
ls -1 log* | wc -l

这意味着每行列出一个文件,然后通过管道将其传输到字计数命令,参数切换到计数行。