7

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

我想用 grep 查找其中包含“INDIANA JONES”的条目:

$ grep -ir 'INDIANA JONES' ./

然后,我想在该词PORTUGAL的 5,000 个字符内找到该词的条目INDIANA JONES。我该怎么做?

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

4 回答 4

3

使用grep'-o标志输出匹配项周围的 5000 个字符,然后在这些字符中搜索第二个字符串。例如:

grep -ioE ".{5000}INDIANA JONES.{5000}" file.txt | grep "PORTUGAL"

如果您需要原始匹配,请将-n标志添加到第二个grep并通过管道输入:

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
于 2013-11-09T01:08:04.120 回答
1

考虑安装 ack-grep。

sudo apt-get install ack-grep

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

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

这可能不是多个字符,而是朝那个方向迈出的一步。

虽然这可能不是一个解决方案,但它可能会给你一些关于如何做到这一点的想法。查找 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

用你需要的替换100s。

第二步:解析输出

您需要获取 ack-grep 返回的匹配项并解析它们,在这些子范围内再次查找任何匹配项。

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

这应该需要更多的工作,可能涉及一个 bash 脚本(我可能会看看我这周是否可以让一个工作),但它通过将其分解为更易于管理的块来解决您的海量数据问题。

于 2013-11-09T00:33:00.403 回答
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
于 2013-11-09T00:44:41.570 回答
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 ...- 搜索葡萄牙。打印文件名 ( -H),但我们告诉我们要使用的“文件名” --label="$filename"。也打印行号,-n.
于 2013-11-09T02:43:41.160 回答