如何在grep中使用grep

7

我有一堆巨大的文本文件,每个文件大约100MB。

我想使用grep命令查找包含“INDIANA JONES”的条目:

$ grep -ir 'INDIANA JONES' ./

接下来,我想找到在距离INDIANA JONES术语的5,000个字符内包含单词PORTUGAL的条目。我该如何做?

# in pseudocode
grep -ir 'INDIANA JONES' ./ | grep 'PORTUGAL' within 5000 char

3
"5000个字符"的要求能否用一定行数来表达? - beroe
4个回答

3
使用grep的-o选项以输出与匹配字符相关的5000个字符,然后在这些字符中搜索第二个字符串。例如:
grep -ioE ".{5000}INDIANA JONES.{5000}" file.txt | grep "PORTUGAL"

如果你需要匹配原始文本,可以在第二个grep命令中添加-n标志,并将其管道输送到:
cut -f1 -d: > line_numbers.txt

那么您可以使用 awk 命令来打印这些行:
awk 'FNR==NR { a[$0]; next } FNR in a' line_numbers.txt file.txt

为避免生成临时文件,可以这样编写代码:
awk 'FNR==NR { a[$0]; next } FNR in a' <(grep -ioE ".{50000}INDIANA JONES.{50000}" file.txt | grep -n "PORTUGAL" | cut -f1 -d:) file.txt

对于多个文件,请使用findbash循环:

for i in $(find . -type f); do
    awk 'FNR==NR { a[$0]; next } FNR in a' <(grep -ioE ".{50000}INDIANA JONES.{50000}" "$i" | grep -n "PORTUGAL" | cut -f1 -d:) "$i"
done

我喜欢这个答案比我的更好。是的,对于新手来说它可能有些复杂,但解析100MB并不是一项微不足道的任务。 - Plasmarob

1
考虑安装ack-grep。
sudo apt-get install ack-grep

ack-grep是grep的更强大的版本。

在没有完整批处理脚本的情况下,除了使用完整的批处理脚本外,对于您的问题没有简单的解决方案(我能想到的),但是您可以使用- A和-B标志在ack-grep上指定要输出的行数,分别为尾随或引导行。

这可能不是字符数,但是朝着那个方向更进了一步。

虽然这可能不是一个解决方案,但它可能会给您一些如何做到这一点的思路。查找类似于ack、awk、sed等的过滤器,并查看是否可以找到具有此类行为标志的过滤器。

ack-grep手册:

http://manpages.ubuntu.com/manpages/hardy/man1/ack-grep.1p.html

编辑:

我认为令人难过的消息是,你可能认为自己正在寻找的是像这样的东西:

grep "\(INDIANA JONES\).\{1,5000\}PORTUGAL" filename

问题在于,即使是在一个小文件上,查询这个可能会超时。我已经用不同的数字使其工作了。这是一个大小的问题。
对于这样大量的文件,你需要分步骤进行。
解决方案:
我知道的唯一解决方案是使用ack-grep的前导和尾随输出。
第1步:你的行有多长?
如果你知道你需要查找多少行(你可以通过几种方式估计/计算),那么你就可以grep第一个grep的输出。根据你的文件内容,你应该能够得到5000个字符的行数的合理上限(如果一行平均有100个字符,50+行应该足够,但如果有10个字符,你需要500+)。
你必须确定最多可以有多少行是5000个字符。你可以猜测或选择一个高范围,但这取决于你。这是你的数据。
有了这个,调用:(如果你需要100行来表示5000个字符)
ack-grep -ira "PORTUGAL" -A 100 -B 100 filename

并且

ack-grep -ira "INDIANA JONES" -A 100 -B 100 filename

用你需要的内容替换100。

步骤2:解析输出

您需要获取 ack-grep 返回的匹配项并解析它们,查找其中任何子范围中的匹配项。

在第一个 PORTUGAL ack-grep 匹配项的输出中查找 INDIANA JONES,在第二组匹配项中查找 PORTUGAL。

这可能需要更多的工作,可能涉及到一个 bash 脚本(我可能会尝试在本周内让其工作),但它可以通过将其分解成更易处理的块来解决您的大数据问题。


谢谢您的建议。使用正则表达式,grep 的正确语法是什么?grep -ir '/INDIANA JONES.{1,5000}PORTUGAL/' ./ - David542
1
GNU和BSD版本的grep都支持-A-B标志。 - iruvar
不,他们没有。也许你没有理解 - 当我说“在 ack-grep 上的 -A 和 -B 标志...”时,我是在谈论它。我认为我表达得很清楚了。 - Plasmarob

1

处理这个问题的一种方法是使用。您可以将记录分隔符设置为INDIANA JONESPORTUGAL,然后对记录执行长度检查(在剥离换行符之后,假设换行符不计入5000的限制)。您可能需要使用来在目录中递归运行此操作。

awk -v RS='INDIANA JONES|PORTUGAL' '{a = $0;
gsub("\n", "", a)};
((RT ~ /IND/ && prevRT ~/POR/) || (RT ~ /POR/ && prevRT ~/IND/)) && length(a) < 5000{found=1};
{prevRT=RT};
END{if (found) print FILENAME}' file.txt

0

grep 'INDIANA JONES' . -iR -l | while read filename; do head -c 5000 "$filename" | grep -n PORTUGAL -H --label="$filename" ; done

这段代码的作用如下:

  • grep 'INDIANA JONES' . -iR -l。在当前目录及其子目录中搜索所有文件,不区分大小写(-i),只打印匹配的文件名(-l),不打印任何内容。
  • | while read filename; do ...|...|...; done 对于每个输入行,将其存储在变量$filename中并执行管道。

现在,对于每个匹配“INDIANA JONES”的文件,我们执行以下操作:

  • head -c 5000 "$filename" - 提取前5000个字符
  • grep ... - 搜索“PORTUGAL”。打印文件名(-H),但我们使用--label="$filename"告诉我们要使用的“filename”。同时打印行号-n

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