我的目录下有大约 500 万个文本文件 - 全部格式相同(没有什么特别的,只是每行包含一些整数的纯文本文件)。我想计算所有这些文件中的最大和最小行数。
我首先尝试像这样写出所有行数(然后练习如何从此列表中找到最小值和最大值):
wc -l `find /some/data/dir/with/text/files/ -type f` > report.txt
但这给我带来了一个错误:
bash: /usr/bin/wc: Argument list too long
也许有更好的方法来解决这个问题?
也许 GNU-Parallel 可以在这里提供帮助?
答案1
您可以使用它find
来生成文件列表并继续通过管道传送它。这可以避免 shell 尝试在单个命令中扩展所有 500 万个文件名
LC_ALL=C find -type f -exec wc -l {} + |
awk '
$2 != "total" {
if (max=="" || $1>max) {max=$1; mxf=$2};
if (min=="" || $1<min) {min=$1; mnf=$2};
}
END { printf "Min %d for %s, max %d for %s\n", min, mnf, max, mxf }
'
find
生成一个列表计算文件名,它被传递给awk
脚本。这反过来又完成了繁重的工作,寻找并报告最大和最小值以及文件名。
这个简单的代码不处理包含空格或非打印字符的文件名。
答案2
使用最新版本的 GNU 实用程序:
(
printf '/dev/null\0' # for the case where's there's only one file
find . -type f -print0
) |
wc --files0-from=- -l |
sed '1d;$d' | # remove /dev/null and total
sort -n |
sed '1b;$b;d'
在这里,我们通过管道将文件列表从find
传递到的 stdin 而不是通过参数,因此它有一些优点:参数数量没有限制,因为我们不使用系统调用。一旦找到文件就可以开始读取它们。与或解决方案相比,只执行一次调用,因此我们最多得到一行。wc
wc
execve()
wc
find
xargs
-exec {} +
wc
total
请注意,GNU wc
8.30 至少会破坏包含换行符的文件名。例如,一个名为 的文件./a<newline>b
被渲染为'./a'$'\n''b'
(此处使用 ksh93 样式$'...'
引用将换行符表示为$'\n'
)。在这种情况下,您可以判断何时wc
进行该修改,因为所有文件路径都应以.
.因此,当您看到 a 时'
,意味着已经执行了重整。
您可以在zsh
shell 中使用其Q
参数扩展标志来撤消它:
$ wc -l './a
b'
146 './a'$'\n''b'
$ !! | read -r length file
$ printf '<%s>\n' $file ${(Q)file}
<'./a'$'\n''b'>
<./a
b>
在一般情况下,您无法判断何时wc
会发生这种损坏,例如它会呈现相同的文件名a<newline>b
或文件名:'a'$'\n''b'
$ wc -l 'a
b' "'a'$'\n''b'"
146 'a'$'\n''b'
1 'a'$'\n''b'
147 total
答案3
xargs
存在是为了处理这种确切的情况,并且只要涉及的文件名不包含空格或换行符就可以工作:
find /some/data/dir/with/text/files/ -type f -print | xargs wc -l
然后您可以根据行数进行排序。如果您不关心哪些特定文件包含最小行数和最大行数,则可以从每行输出中提取行计数字段,将其通过管道传输到uniq
,然后生成的输出文件的第一行是最小行数,最后一行是最大行数。
诚然,这确实涉及在计算您要查找的信息的过程中保留大量数据,因此最好将管道的输出传输find | xargs
到awk
仅运行每一行然后跟踪的脚本如果每行计数小于迄今为止看到的最小值,或大于迄今为止看到的最大值。