【问题标题】:Find specific pattern and print complete text block using awk or sed使用 awk 或 sed 查找特定模式并打印完整的文本块
【发布时间】:2013-10-15 23:02:33
【问题描述】:

如何在文本块中找到特定数字并打印以关键字"BEGIN" 开头并以"END" 结尾的完整 文本块?基本上这就是我的文件的样子:

BEGIN
A: abc
B: 12345
C: def
END

BEGIN
A: xyz
B: 56789
C: abc
END

BEGIN
A: ghi
B: 56712
C: pqr
END

[...]

如果我在寻找'^B: 567',我想得到这个输出:

BEGIN
A: xyz
B: 56789
C: abc
END

BEGIN
A: ghi
B: 56712
C: pqr
END

我可以在这里使用 grep (grep -E -B2 -A2 "^B: 567" file),但我想获得更通用的解决方案。我猜 awksed 或许能够做到这一点!?

谢谢! :)

【问题讨论】:

    标签: regex sed awk


    【解决方案1】:

    您可以取消RS 将记录拆分为空行并检查字符串是否在整个块中匹配:

    awk 'BEGIN { RS = "" } /\nB:[[:space:]]+567/ { print $0 ORS }' infile
    

    它产生:

    BEGIN
    A: xyz
    B: 56789
    C: abc
    END 
    
    BEGIN
    A: ghi
    B: 56712
    C: pqr
    END
    

    【讨论】:

    • 您不需要在$0 ~ /B:[[:space:]]+567/ 中使用$0 ~,并且您应该去掉print $0, ORS 中的逗号,这样您就不会在输出中的每个END 之后添加空格字符。例如,如果B: 567A:... 行上显示为文本,您确实需要锚定B
    • @EdMorton:谢谢。固定。
    • 你真的应该做到\nB 而不仅仅是B
    【解决方案2】:

    这个 awk 应该可以工作:

    awk -v s='B: 567' '$0~s' RS= file
    BEGIN
    A: xyz
    B: 56789
    C: abc
    END
    BEGIN
    A: ghi
    B: 56712
    C: pqr
    END
    

    【讨论】:

    • @EdMorton:任何搜索字符串都可以在这个命令中传递。
    • 确实如此,不过在这个例子中使用 OP 需要的字符串并没有什么坏处。
    【解决方案3】:
    $ awk -v RS= -v ORS='\n\n' '/\nB: 567/' file
    BEGIN
    A: xyz
    B: 56789
    C: abc
    END
    
    BEGIN
    A: ghi
    B: 56712
    C: pqr
    END
    

    注意B 之前的\n 以确保它出现在一行的开头。这代替了您最初拥有的^ 字符串开头字符,因为现在每一行都不是它自己的了细绳。您需要在上面设置 ORS 才能在记录之间重新插入空行。

    【讨论】:

      【解决方案4】:
      perl -lne 'if(/56789/){$f=1}
                 push @a,$_;
                 if(/END/){
                    if($f){print join "\n",@a}
                 undef @a;$f=0}' your_file
      

      【讨论】:

        【解决方案5】:

        这可能对你有用(GNU sed):

        sed -n '/^BEGIN/{x;d};H;/^END/{x;s/^B: 567/&/mp}' file
        

        或者这个:

        sed -n '/^BEGIN/!b;:a;$!{N;/\nEND/!ba};/\nB: 567/p' file
        

        【讨论】:

          【解决方案6】:

          有点长,但是 RS 技巧已经发布了 :-)

          BEGIN {found=0;start=0;i=0}
          
          
          /BEGIN/ {
              start=1
              delete a
          }
          
          /.*567.*/ {found=1}
          
          {
              if (start==1) {
                  a[i++]=$0
              }
          }
          
          /END/ {
              if (found) {
                  for (i in a)
                      print a[i]
              }
              found=0
              start=0
              delete a
          }
          

          输出:

          $ awk -f s.awk input
          BEGIN
          A: xyz
          B: 56789
          C: abc
          END
          BEGIN
          A: ghi
          B: 56712
          C: pqr
          END
          

          【讨论】:

          • 如果 567 出现在任何行的任何位置,将产生错误匹配,并且可以重新排序输出中的文本,因此 END 位于 BEGIN 或任何其他排列之前,由 for (i in a) 提供。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-08-15
          • 1970-01-01
          • 2021-03-23
          • 2013-07-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多