是否有统计文件的bash命令?

是否有统计文件的bash命令?,bash,Bash,是否有一个bash命令来统计与模式匹配的文件数 例如,我想获取目录中与此模式匹配的所有文件的计数:log*对于递归搜索: find . -type f -name '*.log' -printf x | wc -c wc-c将计算find输出中的字符数,-printf x告诉find为每个结果打印一个x 对于非递归搜索,请执行以下操作: find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c 这个简单的一行程序应该适用于任何she

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


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

对于递归搜索:

find . -type f -name '*.log' -printf x | wc -c
wc-c
将计算
find
输出中的字符数,
-printf x
告诉
find
为每个结果打印一个
x

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

find . -maxdepth 1 -type f -name '*.log' -printf x | wc -c

这个简单的一行程序应该适用于任何shell,而不仅仅是bash:

ls -1q log* | wc -l
ls-1q将为每个文件提供一行,即使它们包含空格或特殊字符,如换行符

find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
输出通过管道传输到wc-l,wc-l统计行数。

使用bash可以安全地执行此操作(即不会被带有空格或名称中带有
\n
的文件所干扰):

$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
您需要启用,以便在没有匹配的文件时,不会在
$logfiles
中获取文本
*.log
。(有关如何安全重置的示例,请参见。)


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

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

这将查找所有与log*匹配且不以
*
开头的文件-“not name.*”是redunant,但需要注意的是,“ls”的默认值是不显示点文件,而“find”的默认值是包含它们

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


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

这个问题的公认答案是错误的,但我的代表性较低,因此无法添加评论

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

shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
接受答案的问题是wc-l统计换行符的数量,即使它们在“ls-l”的输出中以“?”的形式打印到终端,也会统计换行符的数量。这意味着当文件名包含换行符时,接受的答案将失败。我已经测试了建议的命令:

ls -l log* | wc -l
即使只有1个文件与模式匹配,且其名称恰好包含换行符,它也会错误地报告值2。例如:

touch log$'\n'def
ls log* -l | wc -l

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

  • 包含空格、换行符或控制字符的文件名
  • 以连字符开头的文件名(想象一个名为
    -l
    的文件)
  • 隐藏文件,以点开头(如果glob是
    *.log
    而不是
    log*
  • 与glob匹配的目录(例如,与
    log*
    匹配的名为
    logs
    的目录)
  • 空目录(即结果为0)
  • 超大目录(列出所有目录可能会耗尽内存)
这里有一个解决方案可以处理所有这些问题:

ls 2>/dev/null -Ubad1 -- log* | wc -l
说明:

  • -U
    导致
    ls
    不对条目进行排序,这意味着它不需要在内存中加载整个目录列表
  • -b
    为非图形字符打印C样式转义符,这将导致换行符打印为
    \n
  • -a
    打印出所有文件,甚至是隐藏的文件(当glob
    log*
    表示没有隐藏的文件时,不需要)
  • -d
    打印出目录而不尝试列出目录的内容,这是
    ls
    通常会做的事情
  • -1
    确保它位于一列上(ls在写入管道时会自动执行此操作,因此严格来说不需要)
  • 2>/dev/null
    重定向stderr,以便如果有0个日志文件,忽略错误消息。(请注意,
    shopt-s nullglob
    将导致
    ls
    列出整个工作目录。)
  • wc-l
    在生成目录列表时使用目录列表,因此
    ls
    的输出在任何时间点都不在内存中
  • -->
    使用
    -->
    将文件名与命令分开,以免被理解为
    ls
    的参数(如果删除了
    log*
shell会将
log*
扩展到完整的文件列表,如果文件太多,可能会耗尽内存,因此通过grep运行它会更好:

ls -Uba1 | grep ^log | wc -l

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

这是我的一行代码

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

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

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

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

您可以在grep上使用模式,以下是我经常做的:

ls log*| awk'结束{打印编号}'


您可以使用shell函数轻松定义此类命令。此方法不需要任何外部程序,也不会生成任何子进程。它不会尝试危险的
ls
解析,并处理“特殊”字符(空格、换行符、反斜杠等)很好。它只依赖于shell提供的文件名扩展机制。它至少与sh、bash和zsh兼容

下一行定义了一个名为
count
的函数,它打印调用它的参数数

count() { echo $#; }
只需使用所需的模式调用它:

count log*
为使全局模式不匹配时的结果正确,必须在扩展发生时设置shell选项
nullglob
(或
failglob
——这是zsh上的默认行为)。可以如下设置
count log*
shopt -s nullglob    # for sh / bash
setopt nullglob      # for zsh
( shopt -s nullglob ; shopt -u failglob ; count log* )
# sh / bash:
# the alias is expanded before the globbing pattern, so we
# can set required options before the globbing gets expanded,
# and restore them afterwards.
count() {
    eval "$_count_saved_shopts"
    unset _count_saved_shopts
    echo $#
}
alias count='
    _count_saved_shopts="$(shopt -p nullglob failglob)"
    shopt -s nullglob
    shopt -u failglob
    count'
count a* b*          # count files which match either a* or b*
count $(jobs -ps)    # count stopped jobs (sh / bash)
find "$FIND_OPTIONS" -exec count {} \+    # count results of a search
<WARNING! DID NOT WORK> </WARNING! DID NOT WORK>
ls | wc -l
ls | grep log | wc -l
ls -1q some_pattern | wc -l
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};