查找包含特定字符串两次或更多的文件

36

我需要找到特定字符串出现两次或更多的文件。

例如,有三个文件:

文件1:

Hello World!

文件2:

Hello World!
Hello !

文件3:

Hello World!
Hello
Hello Again.

我想使用grep命令来搜索“Hello”,并只获取文件2和3。


2
@Melanie Shebel - 我不太确定您在寻找什么。例如,是否应该考虑同一行中的多个匹配。 - fedorqui
我有一些文件,其中一些包含一次“计算完成”,另一些包含两次“计算完成”。我需要提取包含该字符串两次的文件列表。这些字符串出现在不同的行上。 - Melanie Shebel
那么下面的 所有 答案都可以工作。你还需要什么? - Hans Lub
@MelanieShebel 好的。添加赏金是不错的,尽管我猜你可以提出一个新问题,以便更好地控制可能的解决方案和期望的输出。 - fedorqui
8个回答

38
这个怎么样?
grep -o -c Hello * | awk -F: '{if ($2 > 1){print $1}}'

这将告诉我们哪些文件至少包含两行包含单词“Hello”的内容。如果一个文件有一行“Hello Hello World”呢?它不会被列出来。 - bstar55
1
这应该是 ($2 > 1),否则它只会打印具有 3 或更多命中的文件。 - Jotne
@John C 这正是我所需要的!谢谢 - Hubert Léveillé Gauvin
@bstar55 抱歉如果之前表述不够清晰。由于文件的设计方式,这个问题不会成为一个障碍。 - Hubert Léveillé Gauvin

10

由于此问题标记为grep,因此这里提供仅使用该实用程序和bash的解决方案(无需awk):

#!/bin/bash
for file in *
do
  if [ "$(grep -c "Hello" "${file}")" -gt 1 ]
  then
    echo "${file}"
  fi
done

可以写成单行代码:

for file in *; do if [ "$(grep -c "Hello" "${file}")" -gt 1 ]; then echo "${file}"; fi; done

解释

  • 您可以使用任何shell扩展来修改for file in *语句以获取所有数据文件。
  • grep -c返回与模式匹配的行数,即使一行上有多个匹配项仍只计为一个匹配的行数。
  • if [ ... -gt 1 ]测试文件中是否匹配了一行以上。如果是:
  • echo ${file}打印文件名。

3

这个 awk 命令会打印出所有含有两个或两个以上 Hello 的文件名。

awk 'FNR==1 {if (a>1) print f;a=0} /Hello/ {a++} {f=FILENAME} END {if (a>1) print f}' *
file2
file3

2
你需要的是一个 grep 工具,它可以跨越行结束符识别模式(例如“hello”后面跟着任何内容(可能包括换行符),再跟着“hello”)。
由于 grep 按行处理文件,因此单独使用并不适合这个任务 - 除非你将整个文件塞入一行中。
现在,这很容易实现,例如使用 tr 命令,将换行符替换为空格:
if cat $file | tr '\n' ' ' | grep -q 'hello.*hello'
then
   echo "$file matches"
fi

这种方法非常高效,即使在有很多行(比如100000行)的大文件中也是如此,可以通过使用--max-count=1参数调用grep使其在找到匹配项后停止搜索以进一步提高效率。无论两个"hello"是否在同一行上都不重要。


1
另一种方式:

grep Hello * | cut -d: -f1 | uniq -d

搜索包含“Hello”的行;仅保留文件名;仅打印重复项。

第一次使用uniq命令的-d开关!有趣! - F. Hauri - Give Up GitHub

1
阅读您的问题后,我认为您也想在一行中找到案例hello hello。(查找出现两次或更多次特定字符串的文件。)因此,我想出了这个一行代码:
awk -v p="hello" 'FNR==1{x=0}{x+=gsub(p,p);if(x>1){print FILENAME;nextfile}}' *
  • 在上面的代码行中,p 是您想要搜索的模式
  • 如果文件中包含该模式两次或更多次,则会打印文件名。无论它们是在同一行还是不同行。
  • 在处理过程中,检查了一些行之后,如果我们已经找到了两个或更多个模式,请打印文件名并停止处理当前文件,继续处理下一个输入文件(如果仍然有)。如果您有大文件,则这很有帮助。

一个小测试:

kent$  head f*
==> f <==
hello hello world

==> f2 <==
hello

==> f3 <==
hello
hello
SK-Arch 22:27:00 /tmp/test
kent$ awk -v p="hello" 'FNR==1{x=0}{x+=gsub(p,p);if(x>1){print FILENAME;nextfile}}' f*
f
f3

谢谢@Kent!在我的具体例子中,我永远不会连续两次使用相同的字符串,但知道这一点很好。 - Hubert Léveillé Gauvin

0

将数据传输到脚本语言可能有些过度,但通常比仅使用awk更容易。

grep -rnc "Hello" . | ruby -ne 'file, count = $_.split(":"); puts "#{file}: #{count}" if count&.to_i >= 2'

所以针对您的输入,我们得到

$ grep -rnc "Hello" . | ruby -ne 'file, count = $_.split(":"); puts "#{file}: #{count}" if count&.to_i >= 2'

./2: 2
./3: 3

或者省略计数

grep -rnc "Hello" . | ruby -ne 'file, _ = $_.split(":"); puts file if count&.to_i >= 2'

0

grep -c Hello * | egrep -v ':[01]$' | sed 's/:[0-9]*$//'

请将其翻译为中文。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接