【问题标题】:Print several lines between patterns (first pattern not unique)在图案之间打印几行(第一个图案不是唯一的)
【发布时间】:2014-12-08 11:32:21
【问题描述】:

需要 sed/awk/grep/任何可以解决我的任务的帮助。 我有一个大文件,我需要从中提取多个连续的行。

我有开始模式:<DN>

和结束模式:</GR>

中间有几行,像这样:

<DN>234</DN>
<DD>sdfsd</DD>
<BR>456456</BR>
<COL>6575675 sdfsd</COL>

<RAC>456464</RAC>
<GR>sdfsdfsFFFDd</GR>

我试过了:

sed -n '/\<DN\>/,/\<\/GR\>/p'

和其他几个(使用 awk 和 sed)。 它工作正常,但问题是源文件可能包含以&lt;DN&gt; 开头的行,并且在一堆行的末尾没有&lt;/GR&gt;,然后在最后一个正常的部分开始:

<DN>234</DN> - unneded DN
<AB>sdfsd</AB>
<DC>456456</DC>
<EF>6575675 sdfsd</EF>
....really large piece of unwanted text here....

<DN>234</DN>
<DD>sdfsd</DD>
<BR>456456</BR>
<COL>6575675 sdfsd</COL>

<RAC>456464</RAC>
<GR>sdfsdfsFFFDd</GR>
<RAC>456464</RAC>
<GR>sdfsdfsFFFDd</GR>

如何仅提取需要的行并忽略包含&lt;DN&gt; 而没有结束&lt;/GR&gt; 的垃圾日志?

接下来,我需要将多行片段从&lt;DN&gt; 转换为&lt;/GR&gt; 为单行文件,以&lt;DN&gt; 开头并以&lt;/GR&gt; 结尾。 任何帮助,将不胜感激。我卡住了

【问题讨论】:

    标签: regex bash awk sed grep


    【解决方案1】:

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

    sed -n '/<DN>/{h;b};x;/./G;x;/<\/GR/{x;/./p;z;x}' file
    

    使用保持空间存储&lt;DN&gt;&lt;/GR&gt; 之间的行。

    【讨论】:

      【解决方案2】:
      awk '
      # Lines that start with '<DN>' start our matching.
      /^<DN>/ {
          # If we saw a start without a matching end throw everything we've saved away.
          if (dn) {
              d=""
          }
          # Mark being in a '<DN>' element.
          dn=1
          # Save the current line.
          d=$0
          next
      }
      
      # Lines that end with '</GR>$' end our matching (but only if we are currently in a match).
      dn && /<\/GR>$/ {
          # We aren't in a <DN> element anymore.
          dn=0
          # Print out the lines we've saved and the current line.
          printf "%s%s%s\n", d, OFS, $0
          # Reset our saved contents.
          d=""
          next
      }
      
      # If we are in a <DN> element and have saved contents append the current line to the contents (separated by OFS).
      dn && d {
          d=d OFS $0
      }
      ' file
      

      【讨论】:

      • 哇,谢谢,这真的很棒。我什至不知道如何使用这个。我应该将其复制到脚本中吗?如果我这样做,什么都不会发生。系统运行它几秒钟,然后什么也没打印。但是,如果我手动或使用 grep -a 进行检查,文件确实包含匹配的数据。
      • 文件是否有 DOS 行结尾? grep '&lt;/GR&gt;$' 是否找到匹配行? 'grep '^'`吗?
      • 实际上文件没有行尾(至少notepad++没有显示任何CRLF或类似的东西),它有点二进制,但包含大量文本,混合了二进制数据。 grep -a '^' 20140905.log 找到许多匹配项。 grep '$' 20140905.log - 没有
      • @IvanBondarets 如果文件不是“文本文件”,那么在其上使用文本工具不太可靠。话虽如此,您可以尝试在脚本中使用/&lt;\/GR&gt;/(删除$),如果它们不是行尾的话。假设它仍然与您想要的正确匹配。
      【解决方案3】:
      awk '
        /^<DN>/ { n = 1 }
      
        n { lines[n++] = $0 }
      
        n && /<\/GR>$/ {
          for (i=1; i<n; i++) printf "%s", lines[i]
          print ""
          n = 0
        }
      ' file
      

      【讨论】:

      • 谢谢!现在我需要一个如何使用它的建议:) 如果我将它复制到脚本中并将“文件”更改为我的实际文件名 - 什么也不会发生。系统运行了几秒钟并没有打印任何内容。
      • 对于这两种 awk 解决方案,您可以将代码复制粘贴到命令行中,并且只需将 file 输入适当地更改为 myFileInput.txt(或类似的)。 (我会复制到记事本并修复文件名,选择/复制/粘贴到命令行)。更好的解决方案是将其中任何一个保存到文件中,然后使用awk -f solutionN.awk myInputFile.txt调用还有其他几种解决方案,但它们不适合这里;-) 祝你好运。
      【解决方案4】:

      用 bash:

      fun () 
      { 
          local line output;
          while IFS= read -r line; do
              if [[ $line =~ ^'<DN>' ]]; then
                  output=$line;
              else
                  if [[ -n $output ]]; then
                      output=$output$'\n'$line;
                      if [[ $line =~ '</GR>'$ ]]; then
                          echo "$output";
                          output=;
                      fi;
                  fi;
              fi;
          done
      }
      
      fun <file
      

      【讨论】:

        【解决方案5】:

        您可以为此使用pcregrep 工具。

        $ pcregrep -o -M '(?s)(?<=^|\s)<DN>(?:(?!<DN>).)*?</GR>(?=\n|$)' file
        <DN>234</DN>
        <DD>sdfsd</DD>
        <BR>456456</BR>
        <COL>6575675 sdfsd</COL>
        
        <RAC>456464</RAC>
        <GR>sdfsdfsFFFDd</GR>
        

        【讨论】:

        • 感谢您的回复!不幸的是,您的建议不适用于实际文件。也许那是因为系统将其视为二进制文件,我必须在 grep 命令中添加 -a 选项才能使其工作。但是 pcregrep 没有这样的选择。
        • 您能否在 pastebin 或您的问题中发布确切的文件内容?
        • 它太大了,我怕。几百兆字节的文件只包含我需要提取的几行有价值的数据。它来自生产系统,包含客户数据。
        • 这个最后对我有用:pcgrep --buffer-size=100000000 -o -M '(?s)(?(?:( ?!).)*?'
        猜你喜欢
        • 2017-02-08
        • 1970-01-01
        • 2019-09-18
        • 1970-01-01
        • 1970-01-01
        • 2018-07-12
        • 1970-01-01
        • 1970-01-01
        • 2016-06-13
        相关资源
        最近更新 更多