Answers:
这个简单的单行代码应该可以在任何shell中工作,而不仅仅是bash:
ls -1q log* | wc -l
ls -1q将为每个文件提供一行,即使它们包含空格或换行符之类的特殊字符。
输出通过管道传送到wc -l,后者对行数进行计数。
ls
,因为它会创建一个子进程。 log*
由shell扩展,而不是由shell扩展ls
,因此可以轻松echo
完成。
logs
问题的目录中有一个目录,则该日志目录的内容也将计算在内。这可能不是故意的。
您可以\n
使用bash 安全地执行此操作(即不会被空格或名称干扰文件):
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
您需要启用,nullglob
以便*.log
在没有文件匹配的情况下不会在$logfiles
数组中获取文字。(有关如何安全重置它的示例,请参见如何“撤消”“ -x”设置。)
shopt -u nullglob
如果nullglob
未设置决赛,则应该跳过决赛。
*.log
用just 替换*
将计数目录。如果您要枚举的文件具有传统的命名约定name.extension
,请使用*.*
。
这里有很多答案,但有些没有考虑
-l
)*.log
不是log*
logs
相匹配的目录(例如,名为match的目录log*
)这是处理所有这些问题的解决方案:
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
最后一个在不使用大量内存的情况下处理了非常大的文件目录(尽管它确实使用了子外壳程序)。将-d
不再是必要的,因为它仅列出当前目录的内容。
对于递归搜索:
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
-name '*.log'
那么它将计算所有文件,这是我的用例所需要的。另外,-maxdepth标志非常有用,谢谢!
find
;仅打印逐字文件名以外的其他内容。
这个问题的可接受答案是错误的,但是我的代表人数很少,因此无法对其添加评论。
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
如果您有很多文件,并且不想使用优雅的shopt -s nullglob
bash数组解决方案,则可以使用find等,只要您不打印出文件名(其中可能包含换行符)即可。
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
这将查找所有与log *匹配且不以.*
- 开头的文件-“ not name。*”是多余的,但请注意,“ ls”的默认设置是不显示点文件,但默认设置寻找是将它们包括在内。
这是一个正确的答案,它可以处理您可以扔给它的任何类型的文件名,因为该文件名从未在命令之间传递。
但是,shopt nullglob
答案是最好的答案!
find
vs使用ls
是解决问题的两种不同方式。 find
并非总是存在于机器上,而是ls
通常存在,
find
可能并没有所有这些花哨的选择ls
。
-maxdepth 1
find
默认情况下执行此操作。如果您没有意识到有一个隐藏的子文件夹,这可能会造成混乱,并且ls
在某些情况下(默认情况下不会报告隐藏的文件)使用时可能会更有利。
我已经给了这个答案很多想法,尤其是考虑到“ 不解析”之类的东西。一开始,我尝试
<警告!没工作>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</警告!没工作>
如果只有一个文件名像
touch $'w\nlf.aa'
但是如果我这样创建一个文件名则失败
touch $'firstline\n3 and some other\n1\n2\texciting\n86stuff.jpg'
我终于想出了我要介绍的内容。注意我试图获取目录中所有文件的计数(不包括任何子目录)。我认为,连同@Mat和@Dan_Yard的答案,以及至少具有@mogsie提出的大多数要求(我不确定内存。)我认为@mogsie的答案是正确的,但ls
除非有特殊情况,否则我总是尽量避免解析。
awk -F"\0" '{print NF-1}' < <(find . -maxdepth 1 -type f -print0) | awk '{sum+=$1}END{print sum}'
更具可读性:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -print0) | \
awk '{sum+=$1}END{print sum}'
这是专门针对文件的查找,使用空字符分隔输出(以避免空格和换行符问题),然后计算空字符的数量。文件的数量将比空字符的数量少一个,因为末尾将有一个空字符。
为了回答OP的问题,有两种情况需要考虑
1)非递归搜索:
awk -F"\0" '{print NF-1}' < \
<(find . -maxdepth 1 -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
2)递归搜索。请注意,-name
为了稍微不同的行为(隐藏文件等),可能需要更改参数内部的内容。
awk -F"\0" '{print NF-1}' < \
<(find . -type f -name "log*" -print0) | \
awk '{sum+=$1}END{print sum}'
如果有人想评论这些答案与我在此答案中提到的那些答案的比较,请这样做。
请注意,我在获得答案的同时进入了这一思考过程。
(信誉不足,无法发表评论)
这是BUGGY:
ls -1q some_pattern | wc -l
如果shopt -s nullglob
碰巧设置了,它将打印所有常规文件的数量,而不仅仅是带有模式的文件(在CentOS-8和Cygwin上测试)。谁知道还有其他无意义的错误ls
?
这是正确的,并且速度更快:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
它完成了预期的工作。
0.006
在CentOS和0.083
Cygwin上(如果小心使用)。
0.000
在CentOS和0.003
Cygwin上。
您可以使用Shell函数轻松定义此类命令。此方法不需要任何外部程序,并且不会产生任何子进程。它不会尝试危险的ls
解析,并且可以很好地处理“特殊”字符(空格,换行符,反斜杠等)。它仅依赖于Shell提供的文件名扩展机制。它至少与sh,bash和zsh兼容。
下面的行定义了一个称为的函数count
,该函数打印已被调用的参数数量。
count() { echo $#; }
只需使用所需的模式调用它即可:
count log*
为使当globlob模式不匹配时结果正确,必须在扩展发生时设置shell选项nullglob
(或failglob
-这是zsh的默认行为)。可以这样设置:
shopt -s nullglob # for sh / bash
setopt nullglob # for zsh
根据您要计算的内容,您可能还会对shell选项感兴趣dotglob
。
不幸的是,至少要使用bash,在本地设置这些选项并不容易。如果您不想在全局范围内设置它们,最直接的解决方案是以更复杂的方式使用该函数:
( shopt -s nullglob ; shopt -u failglob ; count log* )
如果您想恢复轻量级语法count log*
,或者如果您真的想避免产生子shell,则可以按照以下方式修改内容:
# 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)
通过将该函数转换为可从PATH调用的脚本文件(或等效的C程序),它也可以由诸如find
和的程序组成xargs
:
find "$FIND_OPTIONS" -exec count {} \+ # count results of a search
ls -1 log* | wc -l
这意味着每行列出一个文件,然后将其通过管道切换到单词计数命令,并通过参数切换到计数行。
-l
,因为这需要stat(2)
每个文件,并且出于计数目的不会添加任何内容。