计算有多少文件在最后一行包含一个字符串

Count how many files contain a string in the last line

我想统计当前目录下有多少个文件最后一行有字符串"A"

第一个解决方案:tail -n 1 * | grep \"A\"| wc -l

这工作正常,但当有更多文件时它会工作 bash: /usr/bin/tail: Argument list too long。 有办法解决吗?

如果我还可以选择获取 哪些 个文件包含它,则加分。

编辑:我的文件夹包含 343729 个文件

EDIT2:@tso 在他的评论中有用地指出了文章 I'm getting "Argument list too long". How can I process a large list in chunks?

结果:

@tso 解决方案for f in $(find . -type f); do tail -1 $f|grep \"A\"; done|wc -l 大约需要 20 分钟

@lars 解决方案grep -P "\"A\"*\Z" -r . | wc -l 大约需要 20 分钟

@mklement0 解决方案printf '%s[=15=]' * | xargs -0 sh -c 'tail -q -n 1 "$@" | grep \"A\"' - | wc -l 大约需要 10 分钟

@james 解决方案(在评论中)for i in * ; do awk 'END{if(/a/)print FILENAME}' "$i" ; done 大约需要 25 分钟

@codeforester find . -type f -exec tail -n 1 -- {} + | grep -EB 1 '^[^=]+A' | grep -c '^==>' 需要 >20 分钟。

@mklement0 和@codeforester solutiona 还有一个优点,如果我想更改 grep 模式,第二次我 运行 它需要零时间,我想这是由于某种缓存。

我已经接受了@mklement0 的回答似乎是最快的,但我仍然想提及@tso 和@lars 的贡献,并且根据我个人的知识,这是一个更简单且适应性强的解决方案。

尝试查找:

for f in $(find . -type f); do tail -1 $f|grep PATERN; done|wc -l

使用 GNU awk:

$ cat foo
b
a
$ cat bar
b
b
$ awk 'ENDFILE{if(/a/){c++; print FILENAME}}END{print c}' * 
foo
1

如果 grep 支持 -P 选项,这可能有效:

grep -P "A\Z" -r . | wc -l

参见man pcrepattern。简而言之:

  • \Z 主题末尾的匹配项也匹配主题末尾换行符之前的内容
  • \z 仅在主题末尾匹配

试试 \Z\z

要查看哪些文件匹配,您可以只使用 grep 部分而不使用管道 wc

这将return个文件数:

grep -rlP "A\z" | wc -l

如果你想获取名字,那么只需:

grep -rlP "A\Z"

这样使用 findtailgrep 怎么样?这将比必须循环遍历每个文件更有效。此外,tail -1 将只读取文件的最后一行,因此非常 I/O 高效。

find . -maxdepth 1 -type f -exec tail -n 1 -- {} + | grep -EB 1 '^[^=]+A' | grep -c '^==>'
  • find 将分批调用 tail -1,一次传递 ARG_MAX 个文件名
  • tail 将打印每个文件的最后一行,并在其前面加上模式 "==> file_name <=="
  • grep -EB 1 '^[^=]+A' 将查找模式 A 并获取前一行(它会在查找匹配项时排除 file_name 行)
  • grep -c '^==>' 将计算具有匹配模式的文件数

如果您不需要知道匹配的文件的名称,而只想知道文件的数量,您可以这样做:

find . -maxdepth 1 -type f -exec tail -q -n 1 -- {} + | grep -c 'A'
  • xargs 能够克服最大。 command-line 通过有效地 将调用分批处理 为尽可能少的调用来限制长度。

  • shell的builtins,比如printf,是不是受最大限制。 command-line长度。

了解这一点后,您可以使用以下方法(假设您的 xargs 实现支持 NUL-terminated 输入的 -0 选项,并且您的 tail 实现支持多个文件操作数和 -q 选项以抑制文件名 headers.
这两个假设都适用于这些实用程序的 GNU (Linux) 和 BSD/macOS 实现:

printf '%s[=10=]' * | xargs -0 sh -c 'tail -q -n 1 "$@" | grep \"A\"' - | wc -l