是否有bash命令计算与模式匹配的文件数?
例如,我想获取目录中与此模式匹配的所有文件的计数:log*
答案 0 :(得分:155)
这个简单的单行程序应该适用于任何shell,而不仅仅是bash:
ls -1q log* | wc -l
ls -1q会为每个文件提供一行,即使它们包含空格或特殊字符(如换行符)。
输出通过管道输出到wc -l,它计算行数。
答案 1 :(得分:42)
您可以使用bash安全地执行此操作(即不会被名称中带空格或\n
的文件窃听):
$ shopt -s nullglob
$ logfiles=(*.log)
$ echo ${#logfiles[@]}
您需要启用nullglob
,以便在没有文件匹配的情况下,您无法在*.log
array中获得文字$logfiles
。 (有关如何安全地重置它的示例,请参阅How to "undo" a 'set -x'?。)
答案 2 :(得分:41)
这里有很多答案,但有些人没有考虑到
-l
的文件)*.log
而不是log*
logs
的匹配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
最后一个处理非常大的文件目录而不使用大量内存(虽然它确实使用了子shell)。不再需要-d
,因为它只列出当前目录的内容。
答案 3 :(得分:34)
对于递归搜索:
find . -type f -name '*.log' | wc -l
wc -w
计算输出中的单词数量(bash会将*.log
扩展为与该模式匹配的以空格分隔的文件列表),而wc -l
将计算行数(find
每行打印一个结果。)
对于非递归搜索,请执行以下操作:
find . -maxdepth 1 -type f -name '*.log' | wc -l
答案 4 :(得分:7)
这个问题的接受答案是错误的,但我的代表很低,所以无法添加评论。
这个问题的正确答案由Mat给出:
shopt -s nullglob
logfiles=(*.log)
echo ${#logfiles[@]}
接受答案的问题是,wc -l计算换行符的数量,即使它们以“?”打印到终端,也要对它们进行计数。在'ls -l'的输出中。这意味着当文件名包含换行符时,接受的答案FAILS。我测试了建议的命令:
ls -l log* | wc -l
并且它错误地报告值2,即使只有一个文件匹配其名称恰好包含换行符的模式。例如:
touch log$'\n'def
ls log* -l | wc -l
答案 5 :(得分:6)
如果你有很多文件并且你不想使用优雅的shopt -s nullglob
和bash数组解决方案,只要你不打印文件名就可以使用find等等(可能包含换行符。)
find -maxdepth 1 -name "log*" -not -name ".*" -printf '%i\n' | wc -l
这将找到所有匹配log *并且不以.*
开头的文件 - “not name。*”是冗余的,但重要的是要注意“ls”的默认值不是显示点文件,但查找的默认值是包含它们。
这是一个正确的答案,并处理你可以抛出的任何类型的文件名,因为文件名永远不会在命令之间传递。
但是,shopt nullglob
答案是最好的答案!
答案 6 :(得分:6)
(信誉不足,无法发表评论)
这是 BUGGY :
ls -1q some_pattern | wc -l
如果恰好设置了shopt -s nullglob
,它会打印 ALL 个常规文件的数量,而不仅仅是带有模式(在CentOS-8和Cygwin上测试过)的文件。谁知道ls
还有其他无意义的错误?
这是正确,并且速度更快:
shopt -s nullglob; files=(some_pattern); echo ${#files[@]};
它完成了预期的工作。
0.006
,在Cygwin上为0.083
(如果小心使用)。
0.000
,在Cygwin上为0.003
。
答案 7 :(得分:4)
这是我的一个班轮。
file_count=$( shopt -s nullglob ; set -- $directory_to_search_inside/* ; echo $#)
答案 8 :(得分:1)
您可以使用-R选项来查找文件以及递归目录中的文件
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上使用模式
答案 9 :(得分:1)
您可以使用Shell函数轻松定义此类命令。此方法不需要任何外部程序,并且不会产生任何子进程。它不会尝试进行危险的ls
解析,并且可以很好地处理“特殊”字符(空格,换行符,反斜杠等)。它仅依赖于Shell提供的文件名扩展机制。它至少与sh,bash和zsh兼容。
下面的行定义了一个名为count
的函数,该函数显示已被调用的参数数量。
count() { echo $#; }
只需使用所需的模式进行调用:
count log*
为使glob模式不匹配时结果正确,必须在扩展发生时设置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*
,或者如果您真的想避免产生子外壳,则可以按照以下方式破解某些内容:
# 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
答案 10 :(得分:1)
我给了这个答案很多想法,尤其是考虑到don't-parse-ls stuff。一开始,我尝试过
<WARNING! DID NOT WORK>
du --inodes --files0-from=<(find . -maxdepth 1 -type f -print0) | awk '{sum+=int($1)}END{print sum}'
</WARNING! DID NOT WORK>
如果只有这样的文件名,则可以使用
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}'
如果有人想评论这些答案与我在此答案中所提到的相比,请这样做。
注意,我在获得this answer的同时开始了这个思考过程。
答案 11 :(得分:1)
这是您可以在脚本中使用的通用Bash函数。
# @see https://stackoverflow.com/a/11307382/430062
function countFiles {
shopt -s nullglob
logfiles=($1)
echo ${#logfiles[@]}
}
FILES_COUNT=$(countFiles "$file-*")
答案 12 :(得分:1)
这可以通过标准的POSIX Shell语法完成。
这是一个简单的count_entries
函数:
#!/usr/bin/env sh
count_entries()
{
# Emulating Bash nullglob
# If argument 1 is not an existing entry
if [ ! -e "$1" ]
# argument is a returned pattern
# then shift it out
then shift
fi
echo $#
}
简洁的定义:
count_entries(){ [ ! -e "$1" ]&&shift;echo $#;}
按类型提供的POSIX兼容文件计数器:
#!/usr/bin/env sh
count_files()
# Count the file arguments matching the file operator
# Synopsys:
# count_files operator FILE [...]
# Arguments:
# $1: The file operator
# Allowed values:
# -a FILE True if file exists.
# -b FILE True if file is block special.
# -c FILE True if file is character special.
# -d FILE True if file is a directory.
# -e FILE True if file exists.
# -f FILE True if file exists and is a regular file.
# -g FILE True if file is set-group-id.
# -h FILE True if file is a symbolic link.
# -L FILE True if file is a symbolic link.
# -k FILE True if file has its `sticky' bit set.
# -p FILE True if file is a named pipe.
# -r FILE True if file is readable by you.
# -s FILE True if file exists and is not empty.
# -S FILE True if file is a socket.
# -t FD True if FD is opened on a terminal.
# -u FILE True if the file is set-user-id.
# -w FILE True if the file is writable by you.
# -x FILE True if the file is executable by you.
# -O FILE True if the file is effectively owned by you.
# -G FILE True if the file is effectively owned by your group.
# -N FILE True if the file has been modified since it was last read.
# $@: The files arguments
# Output:
# The number of matching files
# Return:
# 1: Unknown file operator
{
operator=$1
shift
case $operator in
-[abcdefghLkprsStuwxOGN])
for arg; do
# If file is not of required type
if ! test "$operator" "$arg"; then
# Shift it out
shift
fi
done
echo $#
;;
*)
printf 'Invalid file operator: %s\n' "$operator" >&2
return 1
;;
esac
}
count_files "$@"
示例用法:
count_files -f log*.txt
count_files -d datadir*
答案 13 :(得分:0)
ls -1 log* | wc -l
这意味着每行列出一个文件,然后将其传递给字数命令命令,并将参数切换为计数行。
答案 14 :(得分:0)
这就是我一直在做的事情:
ls日志* | awk'END {print NR}'
答案 15 :(得分:-1)
要计数所有内容,只需将ls输送到字数统计行:
ls | wc -l
要使用模式进行计数,请先管道传输到grep:
ls | grep log | wc -l