【发布时间】:2017-02-06 10:26:30
【问题描述】:
从版本 2.25 开始,"bug" in grep 被固定,因此空字节而不是换行符用于终止输出行。 这与捕获和处理多行 grep 匹配的简单性一样好(参见示例)
不幸的是,我在生产中使用 grep 版本 2.20。 这意味着对于处理 \n 终止的日志文件,您无法将 grep 匹配与每一行输出区分开来。
因此我的问题:
当您遇到 2.25 之前的版本时,处理 grep (oz) 命令的每个结果的最有效方法是什么?
(注意:这是一个更复杂脚本的小示例,需要根据请求处理超过 10k 的大日志文件,因此我寻求“最有效”的解决方案)
一个简单的例子:
测试日志
flag test1
flag test2
flag test3
test4
test5
flag test6
test7
flag test8
test.sh
#!/bin/bash
#regex explained:
#(?s)enable multiline pattern search
#(flag) capturegroup with pattern indicating new entry
#[[:blank:]] followed by a space
#(.*?) capturegroup for the rest of the entry, non-greedy
#(?=(?:\r\n|[\r\n])(flag)|\z) positive lookahead:
# - stop when the next newline begins with flag
# - OR if last entry is a match: proceed 'till end of entry
regex_multiline="(?s)(flag)[[:blank:]](.*?)(?=(?:\r\n|[\r\n])(flag)|\z)"
logfile="./test.log"
test1(){
#this works only with grep 2.25 or higher,
#which returns a NULL-byte delimiter after each capture
echo start
while IFS= read -r -d '' line ; do
printf '<test>%s</test>\n' "$line"
done < <(grep -Pzo $regex_multiline $logfile)
echo end
}
test2(){
#I need this to work for each match, instead of each line
echo start
while IFS= read -r line ; do
printf '<test>%s</test>\n' "$line"
done < <(grep -Pzo $regex_multiline $logfile)
echo end
}
测试 1 的结果是我想要的:
start
<test>flag test1</test>
<test>flag test2</test>
<test>flag test3
test4
test5</test>
<test>flag test6
test7
</test>
<test>flag test8</test>
end
测试 2 结果
start
<test>flag test1</test>
<test>flag test2</test>
<test>flag test3</test>
<test> test4</test>
<test> test5</test>
<test>flag test6</test>
<test></test>
<test>test7</test>
<test> </test>
<test>flag test8</test>
end
【问题讨论】:
-
测试 2 失败,因为您在
read中删除了空分隔符-d '',但grep继续产生输出,NULL分隔 -
不,测试 2 失败,
-d '',因为 grep pre 2.25 not 产生空输出,而是 \n。 (如我的 OP 中的链接中所引用)。如果您尝试使用 grep start\nend -
是否允许使用其他工具或必须使用 grep 完成?
-
是的,当然!但是当我开始使用
echo "$this" | cut -d "that"时,性能就会下降。我尝试使用tr '\n '\0' | grep -Pzo etc.扭转局面,但我没能让它在while IFS= read -r -d '\n' line ; do之后继续工作 -
这样的东西有用吗?
tr '\n' '-' <file |sed 's/^flag/<test>\0/g; s/-flag/<\/test>\n<test>\0/g; s/<test>-/<test>/g; $s/$/<\/test>/' |tr '-' '\n'性能好像很差,不过你试试看....
标签: regex linux bash shell grep