【问题标题】:Need grep/awk/gawk to return whole section despite of break lines尽管有断线,但需要 grep/awk/gawk 返回整个部分
【发布时间】:2018-04-25 13:44:01
【问题描述】:

我有以下问题... 我有一个文件,类似于这个:

2018-04-25: line1
2018-04-25: line2
        this is another line
        I'm a line
2018-04-25: line3
2018-04-25: line4

如果我运行:grep 'this' test.log,结果将是:

    this is another line

但我需要的结果是:

2018-04-25: line2
        this is another line
        I'm a line

因为“这是另一行”实际上是同一个条目的一部分,唯一的问题是我们在那里有一个断行,我需要我的 grep 来忽略这个断行。

  • grep -C 1 'this' test.log
  • grep -B 1 'this' test.log

不是一个真正的选择,因为我可能在条目的开头和结尾之间有更多的行/断行。

【问题讨论】:

    标签: linux awk grep find gawk


    【解决方案1】:

    这是使用 GNU awk 的一种方法:行首的日期是记录分隔符。对于包含模式的记录,打印 previous 记录分隔符和当前记录。

    gawk -v RS='(^|\n)[0-9-]{10}' '
        /this/ {sub(/^\n/, "", prev_RT); print prev_RT $0} 
        {prev_RT = RT}
    ' file
    

    或者,更直接

    awk '
        function printif() {if (record ~ /this/) print record}
        /^[0-9-]{10}/ {printif(); record = ""} 
        {record = (record ? record "\n" : "") $0} 
        END {printif()}
    ' file
    

    【讨论】:

    • 谁在这里投反对票?我看不出有什么原因。
    • 嗯,有通常的嫌疑人。
    • 非常神秘的詹姆斯。需要详细说明吗?
    • @glennjackman 某个活跃的成员,他一直对所有未达到预期的问题的答案投反对票。主要是脚本和 shell 工具相关的问题。
    • 嗯,我想为提问者的“罪过”惩罚回答者。艰难的爱。
    【解决方案2】:

    对于给定的示例,这会起作用

    $ gawk -v ORS= -v RS='2018-' '/this/{print RS $0}' ip.txt
    2018-04-25: line2
            this is another line
            I'm a line
    
    • -v ORS=清除输出记录分隔符
    • -v RS='2018-'2018- 设置为输入记录分隔符(假设所有记录的年份相同)
    • /this/{print RS $0}如果记录包含this,打印记录分隔符和记录内容

    【讨论】:

    • 也许是RS="\n2018-",这样可以避免记录条目本身包含2018-的情况。
    • @kvantour 不会涵盖第一行...为了稳健性,请参阅 glenn 的回答..
    • RS='(^|\n)2018-' 会解决这个问题。
    • @karakfa 这将需要更改打印 stmt 或处理 RT ......这将导致类似于 glenn 的答案 :) 我故意尝试根据给定的示例寻求更简单的答案;)跨度>
    【解决方案3】:

    如果这是输入:

    2018-04-25: line1
    2018-04-25: line2
            this is another line
            I'm a line
    2018-04-25: line3
    2018-04-25: line4
    

    你可以使用:grep -A2 line2 file.log,它会返回:

    2018-04-25: line2
            this is another line
            I'm a line
    

    -A 代表after-context,来自男人:

    -A num, --after-context=num
             Print num lines of trailing context after each match. 
    

    或者,如果使用this 作为模式,您可以混合使用-B-A,例如:

    grep -B1 -A1 this file.log
    

    【讨论】:

    • 不能真正使用 A/B,因为行是可变的
    【解决方案4】:

    另一个多行 awk 版本:

    #!/usr/bin/awk -f    
    
    # When the line is starting with the time string
    # a new record is starting...
    /^[[:digit:]]{4}(-[[:digit:]]{2}){2}/ {
        # Check if the (b)uffer matches /this/
        if(b~/this/)
           # ... and print it in that case
           print b
    
        # Empty the buffer in any case
        b="" 
    }
    
    # Append each line to the buffer
    {b=b""ORS""$0}
    

    它应该适用于任何版本的 awk。

    【讨论】:

      【解决方案5】:

      为了完成,我们也可以用sed 以更隐秘的方式做到这一点:

       sed -n '/[-0-9]\{10\}:/{x;/this/p;d};H;${x;/this/p}' <file>
      

      或更短:

       sed -n '/[-0-9]\{10\}:/ba;H;$!b;:a;x;/this/p' <file>
      

      要理解这一点,您需要知道sed 有两个记忆。 模式空间是你进行所有操作的地方,保持空间是一个长期记忆。这个想法是通过在每一行附加H 来在 hold space 中构建记录。但是,如果文件的一行(即 模式空间)包含日期,请检查 保持空间 中的内容并在需要时打印。两个空格的交换是用x完成的。

      一步一步:

      sed -n '                       # -n suppress automatic printing of pattern space
              /[-0-9]\{10\}:/ba;     # did we find a date? if so goto label 'a'
              H;                     # append the line to the hold space
              $!b;                   # did we reach EOF? if not, go to the beginning
              :a;                    # create label 'a'
              x;/this/p              # you found a date or hit the EOF
                                     # swap the patterns with 'x'
                                     # check if it contains /this/
                                     # if so print
              ' <file>
      

      【讨论】:

        【解决方案6】:

        grep-B 1 开头的空格:

        $ grep -B 1 "^ " file
        2018-04-25: line2
                this is another line
                I'm a line
        

        如果空间不够:grep -B 1 -v "^[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}:" file

        【讨论】:

        • 哦,这是@nbari 解决方案的变体(++)。
        【解决方案7】:

        从正则表达式匹配到另一个正则表达式:

        awk '/line2/{f=1} f;/I\47m a line/{f=0}' file 
        
        2018-04-25: line2
                this is another line
                I'm a line
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-06-20
          • 2021-01-30
          • 1970-01-01
          • 1970-01-01
          • 2011-05-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多