计算“查找”结果的最佳方法是什么?

我目前的解决方案将是 find <expr> -exec printf '.' \; | wc -c,但是当有超过10000个结果时,这将花费太长的时间。没有更快/更好的方法来做这件事吗?

117778 次浏览

为什么不呢

find <expr> | wc -l

作为一个简单的便携式解决方案?您的原始解决方案是 产生了一个新的过程printf,对于每个找到的文件,这是非常昂贵的(正如您刚刚发现的)。

请注意,如果您的文件名中嵌入了换行符,那么这将超出计算范围,但是如果您嵌入了换行符,那么我怀疑您的问题运行得更深一些。

试试这个(需要 find-printf支持) :

find <expr> -type f -printf '.' | wc -c

它将比计算线路更可靠和更快。

注意,我使用的是 findprintf,而不是外部命令。


让我们坐一会儿:

$ ls -1
a
e
l
ll.sh
r
t
y
z

我的片段基准:

$ time find -type f -printf '.' | wc -c
8


real    0m0.004s
user    0m0.000s
sys     0m0.007s

完整的台词:

$ time find -type f | wc -l
8


real    0m0.006s
user    0m0.003s
sys     0m0.000s

所以我的解决方案更快 =)(重要的部分是 real行)

这是我的 ~/.bashrc中的 countfiles函数(它相当快,应该适用于 Linux & FreeBSD find,并且不会被包含换行符的文件路径所欺骗; 最后的 wc只计算 NUL 字节数) :

countfiles ()
{
command find "${1:-.}" -type f -name "${2:-*}" -print0 |
command tr -dc '\0' | command wc -c;
return 0
}


countfiles


countfiles ~ '*.txt'

这个解决方案当然比这里的其他一些 find -> wc解决方案要慢,但是如果您除了计算文件名之外还想使用其他方法,那么您可以从 find输出中获得 read

n=0
while read -r -d ''; do
((n++)) # count
# maybe perform another act on file
done < <(find <expr> -print0)
echo $n

这只是对 BashGuide 中的 一个解决方案的一个修改,它通过使用 print0find输出分隔符设置为 NUL 字节,并使用 ''(NUL 字节)作为循环分隔符读取它,从而正确地处理具有非标准名称的文件。

符合 POSIX 标准和防换行:

find /path -exec printf %c {} + | wc -c

而且,根据我在 /中的测试,甚至不会比其他解决方案慢两倍,这些解决方案要么不具备换行功能,要么不具备可移植性。

注意 +而不是 \;。这对性能至关重要,因为 \;为每个文件名生成一个 printf命令,而 +为单个 printf命令提供尽可能多的文件名。(在可能存在太多争论的情况下,Find 会根据需要生成新的 Printfs 来处理它,因此就好像

{
printf %c very long argument list1
printf %c very long argument list2
printf %c very long argument list3
} | wc -c

被召唤。)

我需要一些不需要从 find 获取所有输出的东西,因为其他一些命令也运行 print 内容。

在不需要临时文件的情况下,这只能通过一个大的警告来实现: 您可能会得到(远远)不止一行的输出,因为它将对每800 ~ 1600个文件执行一次 output 命令。

find . -print -exec sh -c 'printf %c "$@" | wc -c' '' '{}' + # just print the numbers
find . -print -exec sh -c 'echo "Processed `printf %c "$@" | wc -c` items."' '' '{}' +

产生以下结果:

Processed 1622 items.
Processed 1578 items.
Processed 1587 items.

另一种方法是使用临时文件:

find . -print -fprintf tmp.file .
wc -c <tmp.file # using the file as argument instead causes the file name to be printed after the count


echo "Processed `wc -c <tmp.file` items." # sh variant
echo "Processed $(wc -c <tmp.file) items." # bash variant

每个 find 命令中的 -print根本不会影响计数。