匹配 AWK 中多行的正则表达式。 && 操作员?

Posted

技术标签:

【中文标题】匹配 AWK 中多行的正则表达式。 && 操作员?【英文标题】:Matching regex of multiple lines in AWK. && operator? 【发布时间】:2012-09-24 23:13:16 【问题描述】:

我不确定 && 运算符是否适用于正则表达式。我要做的是匹配一行,使其以数字开头并具有字母“a”并且下一行以数字开头并具有字母“b”和下一行...字母“c” .此 abc 序列将用作开始读取文件的唯一标识符。

这就是我在 awk 中的目标。

/(^[0-9]+ .*a)&&\n(^[0-9]+ .*b)&&\n(^[0-9]+ .*c) 
print $0

这些正则表达式中只有一个像 (^[0-9]+ .*a) 一样工作,但我不知道如何将它们串在一起,下一行就是这样。

我的文件会是这样的:

JUNK UP HERE NOT STARTING WITH NUMBER
1     a           0.110     0.069          
2     a           0.062     0.088          
3     a           0.062     0.121          
4     b           0.062     0.121          
5     c           0.032     0.100         
6     d           0.032     0.100          
7     e           0.032     0.100   

而我想要的是:

3     a           0.062     0.121          
4     b           0.062     0.121          
5     c           0.032     0.100         
6     d           0.032     0.100          
7     e           0.032     0.100 

【问题讨论】:

对于您的情况,因为您的“子句”(您想要 AND 在一起的三个条件)不重叠,所以您根本不需要任何运算符,只需“吃掉”其余的@m.buettner 建议的方式。如果您的条件确实重叠,例如如果您想检查一行是否包含一个符号和一个数字(但您不知道顺序),那么您将使用所谓的“前瞻断言”来实现这种匹配。 只有我知道的前瞻断言是 python 中的 next() 函数。我在下面的答案中尝试过。 我不熟悉 Python,但我说的是前瞻和后瞻结构,我知道 Python 支持:regular-expressions.info/lookaround.html 【参考方案1】:

[根据澄清更新。]

一个高阶位是 Awk 是一种面向行的语言,因此您实际上无法进行正常的模式匹配以跨越行。执行此类操作的通常方法是分别匹配每一行,并让后面的子句/语句确定是否所有正确的部分都已匹配。

我在这里所做的是在一行的第二个字段中查找a,在另一行的第二个字段中查找b,并在第三行的第二个字段中查找c。在前两种情况下,我隐藏了行的内容以及它出现的行号。当第三行匹配并且我们还没有找到整个序列时,我返回并检查其他两行是否存在以及是否具有可接受的行号。如果一切正常,我打印出缓冲的前几行并设置一个标志,指示应该打印其他所有内容。

这是脚本:

$2 == "a"  a = $0; aLine = NR; 
$2 == "b"  b = $0; bLine = NR; 
$2 == "c" && !keepPrinting 
    if ((bLine == (NR - 1)) && (aLine == (NR - 2))) 
        print a;
        print b;
        keepPrinting = 1;
    

keepPrinting  print; 

这是我测试过的一个文件:

JUNK UP HERE NOT STARTING WITH NUMBER
1     a           0.110     0.069
2     a           0.062     0.088
3     a           0.062     0.121
4     b           0.062     0.121
5     c           0.032     0.100
6     d           0.032     0.100
7     e           0.032     0.100
8     a           0.099     0.121
9     b           0.098     0.121
10    c           0.097     0.100
11    x           0.000     0.200

这是我运行它时得到的结果:

$ awk -f blort.awk blort.txt
3     a           0.062     0.121
4     b           0.062     0.121
5     c           0.032     0.100
6     d           0.032     0.100
7     e           0.032     0.100
8     a           0.099     0.121
9     b           0.098     0.121
10    c           0.097     0.100
11    x           0.000     0.200

【讨论】:

这和我想要的差不多。我应该提到 abc 将是我文件中的唯一序列。我会用它作为阅读的起点。所以我想从你的测试文件中得到的输出是带有 a,b,c,d,e,a,b,c,x 的行 我根据您的 cmets 更新了我的答案。从学术角度来看,您发布的状态机解决方案很有趣,但也许像这样的解决方案更实用? 谢谢 danfuzz。对我来说,向老板解释脚本比状态机更容易。我所做的只是添加 if ((keepPrinting > 0) && (++keepPrinting 【参考方案2】:

不,它不起作用。你可以试试这样的:

/(^[0-9]+.*a[^\n]*)\n([0-9]+.*b[^\n]*)\n([0-9]+.*c[^\n]*)/

然后根据需要对尽可能多的字母重复此操作。

[^\n]* 将尽可能多地匹配一行中的非换行符(直到换行符为止)。

【讨论】:

不,没有。谢谢你告诉我 只是为了好玩,尝试在所有三个实例中将 [^\n] 替换为 . 不,我的意思是删除 [^\n] 并使用 . ...如果不这样做,不幸的是,我今天没有想法 如何将多个换行符放入$0?默认情况下,awk 一次读取一行,因此$0 永远不会包含您要查找的换行符。 啊,好吧。我不熟悉awk,所以我只是假设他想要的正则表达式可以将他给定的输入作为一个字符串处理。【参考方案3】:

一位朋友为我编写了这个 awk 程序。它是一个状态机。它有效。

#!/usr/bin/awk -f

BEGIN 
    # We start out in the "idle" state.
    state = "idle"


/^[0-9]+[[:space:]]+q/ 
    # Everytime we encounter a "# q" we either print it or go to the
    # "q_found" state.
    if (state != "printing") 
        state = "q_found"
        line_q = $0
    


/^[0-9]+[[:space:]]+r/ 
    # If we are in the q_found state and "# r" immediate follows,
    # advance to the r_found state.  Else, return to "idle" and 
    # wait for the "# q" to start us off.
    if (state == "q_found") 
        state = "r_found"
        line_r = $0
     else if (state != "printing") 
        state = "idle"
    


/^[0-9]+[[:space:]]+l/ 
    # If we are in the r_found state and "# l" immediate follows,
    # advance to the l_found state.  Else, return to "idle" and 
    # wait for the "# q" to start us off.
    if (state == "r_found") 
        state = "l_found"
        line_l = $0
     else if (state != "printing") 
        state = "idle"
    


/^[0-9]+[[:space:]]+i/ 
    # If we are in the l_found state and "# i" immediate follows,
    # we're ready to start printing.  First, display the lines we
    # squirrelled away then move to the "printing" state.  Else,
    # go to "idle" and wait for the "# q" to start us off.
    if (state == "l_found") 
        state = "printing"
        print line_q
        print line_r
        print line_l
        line = 0
     else if (state != "printing") 
        state = "idle"
    


/^[0-9]+[[:space:]]+/ 
    # If in state "printing", print 50 lines then stop printing
    if (state == "printing") 
        if (++line < 48) print
    

【讨论】:

以上是关于匹配 AWK 中多行的正则表达式。 && 操作员?的主要内容,如果未能解决你的问题,请参考以下文章

Python: 正则表达式匹配多行,实现多行匹配模式

如何使用 awk 打印匹配的正则表达式模式?

正则表达式学习之grep,sed和awk

Linux AWK学习

awk从入门到入土正则匹配

awk 正则 去掉回车换行