计算有多少文件在最后一行包含一个字符串
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"
这样使用 find
、tail
和 grep
怎么样?这将比必须循环遍历每个文件更有效。此外,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
我想统计当前目录下有多少个文件最后一行有字符串"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"
这样使用 find
、tail
和 grep
怎么样?这将比必须循环遍历每个文件更有效。此外,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