我想以一种不贪婪的方式抓取一个文件中的多行,该文件的模式具有开始和结束标签。
例如我有以下输入:
文件.txt
START
test1
test2
foo
END
some
more text
START
test3
bar
test4
test5
END
even more
START
baz
test6
END
现在我想寻找酒吧并打印出之间的所有内容开始和结尾,这样我就会得到:
START
test3
bar
test4
test5
END
到目前为止我所拥有的是以下 grep 命令:
grep -Pzo '(?s)START.*?bar.*?END' file.txt
问题是,这个表达式是贪婪的并打印出:
START # starts at first "START"-tag, not the next one
test1 #
test2 #
foo #
END #
some #
more text #
START
test3
bar
test4
test5
END
grep 标志还没有完成--上下文之前/--后上下文,因为前后的行数可能不同。
文本处理所使用的工具并不重要。它应该可以在一般的 RedHat 系统上运行。此外,工具抓取线条的速度越快,效果就越好。因为我有大约 150MB 的大日志文件。
有人可以告诉我,如何以最好的方式实现我的目标?
更新:
好的,我明白了。我只需要考虑如何从don_crissti
s 链接构建我的命令。这是解决方案:
ed -s file.txt <<< $'g/bar/?START?,/END/p\nq\n'
非常感谢您的快速帮助!
是的,最后它是重复的......
答案1
我认为你的问题是你的非贪婪匹配仍然可以吞噬比你想要的更多的东西,即。结尾沙开始s。这似乎有效:
grep -Pzo '(?s)START(?:(?!END).)*?bar(?:(?!START).)*?END' file.txt
它涵盖了示例中的所有情况,并且如果您是完整的>> file.txt
bar
START
test7
END
仍然有效。
答案2
我会使用 awk,您可以在其中指定记录分隔符。如果记录分隔符是“END”(在其自己的行上),则查找包含“bar”的记录:
awk 'BEGIN {RS = ORS = "\nEND\n"} /bar/' file.txt
处理出现在 START 和 END 标记之间的文本。此更改感觉很hacky,但它适用于这种情况:使用 END 作为记录分隔符,删除 START 关键字之前的任何文本
awk '
BEGIN {RS = ORS = "\nEND\n"}
{sub(/^.*\nSTART\n/, "START\n")}
/bar/
' file.txt
如果“START”在 END 之前出现多次,则可能不会给出所需的结果
foo
START
hello
START
bar
world
END
baz
将输出为
START
bar
world
END
答案3
perl -nE 'BEGIN {$/="\nEND\n"} say /(START.*test.*)/s'
正如@bobbel 指出的,替换say
为print
以避免空行分隔符。