【问题标题】:Selecting lines between marker patterns where one pattern may occur twice选择标记图案之间的线条,其中一种图案可能出现两次
【发布时间】:2020-06-30 01:03:14
【问题描述】:

如果我有一个包含一些文本数据的文件,例如

PATTERN1
TEXT1
PATTERN1
TEXT2
PATTERN2

如何从我知道 PATTERN1 和 PATTERN2 的文件中选择 TEXT2 数据? 我曾尝试使用 here 中提到的 awk,但它会同时打印 TEXT1 和 TEXT2。

【问题讨论】:

    标签: shell unix awk sed


    【解决方案1】:

    如果 TEXT2 总是被 PATTERN1 和 PATTERN2 包围,你可以使用 grep:

    grep -B2 "PATTERN2" file | grep -A1 "PATTERN1" | grep -v "PATTERN1"
    
    • grep -B2 "PATTERN2" -> 抓取 PATTERN2 和前面两行
    • grep -A1 "PATTERN1" -> 从这三行中,抓取 PATTERN1 和后面的行
    • grep -v "PATTERN1" -> 去掉包含 PATTERN1 的行,剩下的是 TEXT2

    【讨论】:

      【解决方案2】:
      $ awk '
      inBlock {
          if ( /PATTERN2/ ) {
              printf "%s", block
              inBlock = 0
          } else {
              block = block $0 ORS
          }
      }
      /PATTERN1/ {
          inBlock = 1
          block = ""
      }
      ' file
      TEXT2
      

      【讨论】:

        【解决方案3】:

        如果PATTERN2 可以出现多次,则仅提取内部文本:

        sed '/PATTERN1/h;//!H;/PATTERN2/!d;//{x;/PATTERN1/!d}'
        

        如果PATTERN2只能出现一次,你可以使用这样的sed脚本:

        sed -n '/PATTERN1/h;//!H;/PATTERN2/{x;p}' input_file.txt
        

        或:

        sed '/PATTERN1/h;//!H;/PATTERN2/!d;//x'
        

        您可以反转行,然后使用 sed 和 2 个地址并再次反转行:

        tac input_file.txt | sed -n '/PATTERN2/,/PATTERN1/p' | tac
        

        使用sed -z,我们可以删除模式前后的所有内容,因为正则表达式是贪婪的:

        sed -z 's/.*\(PATTERN1\n\)/\1/;s/\(PATTERN2\n\).*/\1/g'
        

        【讨论】:

        • 如果我针对示例运行第一个解决方案,结果同时包含 PATTERN1PATTERN2,我的印象是 OP 希望删除这些。
        【解决方案4】:

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

        sed '/PATTERN1/{z;x;d};/PATTERN2/!{H;d};g;s/.//p;d' file
        

        如果当前行包含PATTERN1,则清除该行并删除保留空间(HS)。

        如果当前行不包含PATTERN2,则将其附加到HS并删除该行。

        如果当前行包含PATTERN2,则将其替换为HS的内容,删除第一个字符(将是引入的换行符),打印结果并删除该行。

        替代方案:

        sed -En '/PATTERN1/{:a;/PATTERN1/z;N;/PATTERN2/!ba;s/.(.*)\n.*/\1/p}' file
        

        第一个解决方案假定文件将包含PATTERN1PATTERN2,第二个不包含。

        【讨论】:

          【解决方案5】:

          Perl 来救援!

          perl -ne 'print(@buffer), $inside = @buffer = () if /PATTERN2/;
                    push @buffer, $_ if $inside;
                    @buffer = (), $inside = 1 if /PATTERN1/;
          ' -- file.txt
          

          我们在@buffer 中保留了一组要输出的行。如果我们遇到了 PATTERN1,但还没有遇到 PATTERN2,我们还会保留一个设置为 true 的标志 $inside。

          • 如果我们看到 PATTERN2,我们会打印缓冲区并清除标志。
          • 如果我们在里面,我们会记住当前行。
          • 如果我们看到 PATTERN1,无论我们之前是否看过它,我们都会清除缓冲区并设置标志。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-02-08
            • 1970-01-01
            • 2014-07-23
            • 2018-12-26
            • 1970-01-01
            • 1970-01-01
            • 2015-10-28
            • 2014-01-06
            相关资源
            最近更新 更多