regex - 如何处理每个结果 - 而不是 grep (oz) 命令的行(早于 2.25)

标签 regex linux bash shell grep

从版本 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

测试.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

最佳答案

我认为您最好在这里使用 perl 而不是 grep。您可以使用几乎不修改的正则表达式1,只需将其替换为\1\x002:

regex_multiline="(?s)(flag[[:blank:]].*?)(?=(?:\r\n|[\r\n])flag|\z)"
perl -0777 -pe "s/$regex_multiline/\1\x00/g" < "$logfile"

1您的正则表达式有点奇怪,捕获组在您的 grep 命令的上下文中没有执行任何操作(例如 (flag))。我只是把你要匹配的整个部分放在一组,这样它就对应于替换部分中的\1。根据需要进行调整/我遗漏了一些东西。

2使用 \1\0(对于“匹配组一”、“空字节”)实际上也有效,但这似乎有点令人困惑。

关于regex - 如何处理每个结果 - 而不是 grep (oz) 命令的行(早于 2.25),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42065453/

相关文章:

Javascript 正则表达式仅使用 <li> 标记内的文本替换 <ul> 或 <ol>

c++ - 解析 CSS 样式表

linux - jsvc.exec 错误 : Still running according to PID file

windows - 控制台在 Git Bash 的全新安装中关闭

java - 提取字符串、数字组、下一个字符串、下一个数字组

c - Linux IPC 管道不工作

linux - Bash 列出两个日期之间的日期时间

bash - 如何在 bash 中执行字符串变量的变量扩展?

php - Composer 忽略 $COMPOSER_HOME

c# - 如果术语在搜索文本中被换行符打断,我如何使用 RegEx 查找术语