如何从一行中提取多个非贪婪的正则表达式匹配部分?

时间:2015-12-22 04:05:39

标签: regex linux perl shell command-line

如果我有这样的文件:

abc defghaijkb,mnaobpqa
pbqaaa
qrs - a .. b ...
cde

如何提取以a开头并以b开头的所有部分(我选择这些字符来简化示例,它们可能会被一些更复杂的正则表达式替换)?这是一个理想的输出:

ab
aijkb
aob
a .. b

(将每个项目放在一个单独的行)。由于(g)awk中没有非贪婪的匹配(.*?),我找不到如何解决这个问题(例如使用split)。

注1:不需要使用多行匹配 - 也就是说,regex1regex2之间不允许使用换行符。

注意2:我不想使用sed,我想知道是否可以使用awk,bash或其他一些处理输入文件的命令行工具来完成此操作line ... AWK似乎是一个很好的解决方案,但是...如果它只支持非贪婪的.*?

注3:我无法使用grep,因为在处理大文件时,我总是遇到memory exhausted错误。

注意4.以下是更复杂的regex1regex2的示例。如果他们可以包含非贪婪.*?怎么办?例如。 <a>.*?<b>.*?</b>.*?</a>

更新。更复杂的例子:

[a]text1[a]text000[b]text2[/b]text11[/a]c defgh[a]text3[b]text33[/b]text333[/a]...[/a],mnaobpqa
...[b]aa[/b]bb[/a],,,
qa - [a][b][/b][/a] aabbcc ...
cde

期望的输出:

[a]text000[b]text2[/b]text11[/a]
[a]text3[b]text33[/b]text333[/a]
[a][b][/b][/a]

3 个答案:

答案 0 :(得分:2)

Pure AWK hackery:

awk 'BEGIN{RS="a"}/b/&&NR!=1{sub(/b.*/,"");if($0!~"\n")print"a"$0"b"}'
  • a拆分文件并忽略第一段(前a)。
  • 如果细分中没有b,请忽略它。
  • 首先切断所有内容b并进一步切断。
  • 如果细分受众群中有换行符,请忽略它。
  • 重建截止"a""b"并打印。

我不认为你应该永远使用它。使用perl - 它出现在几乎任何存在awk的系统上,并使这项任务变得轻而易举:

perl -ne 'print map { "$_\n" } /a.*?b/g;'

这甚至适用于grep不支持PCRE的系统,因为Perl的定义支持PCRE。 (我不知道内存耗尽的错误 - 正如rici所说,非病态的正则表达式不应该发生。)

编辑以回应OP提出的其他问题:

&#34;有能力的工具&#34;是任何支持非贪婪运算符和每行多个匹配的东西 - 在这种情况下,perl是无处不在,表达性和速度之间的最佳折衷。

编写的行是一个过滤器 - 您在标准输入中提供输入,您可以在标准输出中输出 - 就像您使用awksed一样。

标准的regexp语法适用:方括号和斜杠需要转义。

perl -ne 'print map { "$_\n" } /\[a\].*?\[b\].*?\[\/b\].*?\[\/a\]/g;' <infile >outfile

答案 1 :(得分:1)

这可以使用grep和现代BSD grep(例如Mac OS上的那个)来实现。

grep -E "a.*?b" -o file

.*?执行非贪婪的匹配。

在只有GNU grep的平台上,可能需要使用-P而不是-E;在基线-POSIX平台或SysV派生的Unixen上,这可能根本不起作用(因为POSIX ERE没有指定非贪婪匹配,the POSIX standard for grep没有定义-o)。

答案 2 :(得分:1)

搜索本身可以用Awk编写:

$ awk '{
    split($0, line, "")
    m=""
    for(i in line) {
        if(line[i] == "a")
            m=line[i]
        else if(m)
            m=m line[i]
        if(m && line[i] == "b") {
            print m
            m=""
        }
    }
}' file
ab
aijkb
aob
a .. b