不要使用grep将相邻匹配的上下文合并在一起

10
如果我在以下文件上运行grep -C 1 match

a
b
match1
c
d
e
match2
f
match3
g

我获得了以下输出:

b
match1
c
--
e
match2
f
match3
g

可以看到,由于相邻匹配“match2”和“match3”的上下文重叠,它们被合并了。不过,我更希望针对每个匹配获取一个完整的上下文描述,这可能需要在上下文报告中复制输入中的行。在这种情况下,我的要求是:

b
match1
c
--
e
match2
f
--
f
match3
g

如何最好地实现这一点?我希望解决方案足够通用,以便轻松适应其他 grep 选项(不同的 -A、-B、-C 值或完全不同的标志)。理想情况下,我希望有一种聪明的方式可以仅使用 grep 来做到这一点....

4个回答

3

我认为使用普通grep不可能做到这一点。

你有使用过Python吗?在我看来,它是一个非常适合处理这类任务的语言(下面的代码段适用于Python 2.7和3.x):

with open("your_file_name") as f:
   lines = [line.rstrip() for line in f.readlines()]
   for num, line in enumerate(lines):
      if "match" in line:
         if num > 0:
            print(lines[num - 1])

         print(line)

         if num < len(lines) - 1:
            print(lines[num + 1])
            if num < len(lines) - 2:
               print("--")

这让我感到:
b
match1
c
--
e
match2
f
--
f
match3
g

这需要针对更复杂的 grep 使用进行调整。仅因为缺少此选项而不得不以简单的方式重新实现匹配(grep 以非常聪明的方式实现了它),这感觉不够令人满意。无论如何,感谢您的建议! - a3nm

3

我认为使用简单的grep命令无法实现这个目标。

下面的sed结构在某种程度上可以解决问题,现在我只需要想办法添加“--”分隔符。

$ sed -n -e '/match/{x;1!p;g;$!N;p;D;}' -e h log
b
match1
c
e
match2
f
f
match3
g

1
这是他已经得到的输出; 他需要其他东西。请在回答之前阅读问题。 - Ernest Friedman-Hill
这并不完美,因为如果您想要不同的-C、-A或-B值,甚至使用其他grep选项,您将不得不以非明显的方式更改sed表达式。 - a3nm
1
@a3_nm,这是一个合理的解决方案要求,但它并没有出现在原始问题中。我建议您编辑您的问题并添加它。 - brandizzi
你所需要的只是一个输出的修饰器。你知道你要搜索的标记 - 这足以提供重复出现标记的行,插入 -- 的信息。我认为这个 sed 解决方案会让你朝着正确的方向前进。你可以编写一个简单的 shell 脚本,使用他的想法调用 grep,传递任意参数并满足你的问题。 - Josh

1
我建议修补grep而不是绕过它。在GNU grep 2.9的src/main.cpp中:
933       /* We print the SEP_STR_GROUP separator only if our output is
934          discontiguous from the last output in the file. */
935       if ((out_before || out_after) && used && p != lastout && group_separator)
936         {
937           PR_SGR_START_IF(sep_color);
938           fputs (group_separator, stdout);
939           PR_SGR_END_IF(sep_color);
940           fputc('\n', stdout);
941         }
942 

这里只需要一个简单的附加标志。

编辑:好吧,当然不是那么简单了,因为grep不能复制上下文,只能添加更多的分隔符。由于grep的线性特性,整个补丁可能并不那么容易。尽管如此,如果您有一个好的补丁案例,它可能是值得的。


0

使用grep或GNU grep似乎不可能。但是,使用标准的POSIX工具和像bash这样的好shell可以获得所需的输出。
注意:解决方案不应需要python或perl。最坏情况下,使用awk或sed。

我快速原型化的一个解决方案类似于这样(它确实涉及重新读取文件的开销,而此解决方案取决于是否允许此开销,原始问题使用-1作为上下文行数的固定数字,这使得可以简单地使用head和tail):

$ OIFS="$IFS"; lines=`grep -n match greptext.txt | /bin/cut -f1 -d:`; 
for l in $lines; 
do IFS=""; match=`/bin/tail -n +$(($l-1)) greptext.txt | /bin/head -3`; 
echo $match; echo "---"; 
done; IFS="$OIFS"

这可能与一些边角情况有关,并且当可能不必要时,会重置IFS,尽管它是一个提示,试图使用POSIX shell和工具的强大功能而不是高级解释器来获得所需的输出。

观点:所有良好的操作系统都具有grep、awk、sed、tr、cut、head、tail、more、less、vi作为内置工具。在最好的操作系统上,它们位于/bin目录下。


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