【问题标题】:Extract lines between 2 tokens in a text file using bash使用 bash 提取文本文件中 2 个标记之间的行
【发布时间】:2011-06-18 22:47:50
【问题描述】:

我有一个如下所示的文本文件:

random useless text 
<!-- this is token 1 --> 
para1 
para2 
para3 
<!-- this is token 2 --> 
random useless text again

我想提取标记之间的文本(当然不包括标记)。我尝试使用 ## 和 %% 来提取两者之间的数据,但它不起作用。我认为它不适用于处理如此大的文本文件。有什么建议我该怎么做?也许是 awk 或 sed ?

【问题讨论】:

    标签: bash


    【解决方案1】:

    无需headtailgrep 或多次读取文件:

    sed -n '/<!-- this is token 1 -->/{:a;n;/<!-- this is token 2 -->/b;p;ba}' inputfile
    

    解释:

    • -n - 不要隐式打印
    • /&lt;!-- this is token 1 --&gt;/{ - 如果找到起始标记,则
      • :a - 标签“a”
        • n - 阅读下一行
        • /&lt;!-- this is token 2 --&gt;/q - 如果是结束标记,退出
        • p - 否则,打印该行
      • ba - 分支到标签“a”
    • }结束如果

    【讨论】:

    • 在您的 sed 脚本中,您使用 b 退出循环,但在您的解释中,您使用了 q(我在使用您的说明时注意到了这一点,q 似乎使 sed 立即退出,而b 将退出循环,但继续寻找下一个 token 1 标记。
    • 我注意到的另一件事:使用 FreeBSD sed,sed -n '/^----$/{n;/^----$/q;p;}' /dev/null 工作正常(无输出),但添加循环(即sed -n '/^----$/{:a;n;/^----$/q;p;ba}' /dev/null)使 sed 产生 unexpected EOF (pending }'s)。我必须在多行中使用循环写出版本。 :-(
    • @FrerichRaabe:对于问题中的示例文本,在我的系统上,bq 具有相同的效果。我以两种方式发布它的事实是偶然的。 Sed 因系统而异。这可能会在 FreeBSD 上为您工作(全部在一条线上):sed -n -e '/&lt;!-- this is token 1 --&gt;/{' -e ':a' -e 'n' -e '/&lt;!-- this is token 2 --&gt;/b' -e 'p' -e 'ba' -e '}'
    • @DennisWilliamson:嘿,拆分脚本是一个聪明的解决方法。我会试一试!顺便说一下,为您的回答 +1,我认为“sed”被严重低估了!
    • @DennisWilliamson 如何使用 bash 变量而不是 &lt;!-- this is token 2 --&gt;
    【解决方案2】:

    您可以提取它,包括使用 sed 的标记。然后使用 head 和 tail 将标记剥离。

    ... | sed -n "/这是令牌 1/,/这是令牌 2/p" |头-n-1 | tail -n+2

    【讨论】:

    • 似乎在 MacOS 上使用负行数作为头部结果 head: illegal line count -- -1
    【解决方案3】:

    尝试以下方法:

    sed -n '/<!-- this is token 1 -->/,/<!-- this is token 2 -->/p' your_input_file
            | egrep -v '<!-- this is token . -->'
    

    【讨论】:

      【解决方案4】:

      也许 sed 和 awk 有更优雅的解决方案,但我有一个“穷人”的方法,包括 grep、cut、head 和 tail。

      #!/bin/bash
      
      dataFile="/path/to/some/data.txt"
      startToken="token 1"
      stopToken="token 2"
      
      startTokenLine=$( grep -n "${startToken}" "${dataFile}" | cut -f 1 -d':' )
      stopTokenLine=$( grep -n "${stopToken}" "${dataFile}" | cut -f 1 -d':' )
      
      let stopTokenLine=stopTokenLine-1
      let tailLines=stopTokenLine-startTokenLine
      
      head -n ${stopTokenLine} ${dataFile} | tail -n ${tailLines}
      

      【讨论】:

        【解决方案5】:

        无需调用强大的 sed/awk/perl。您可以“仅使用 bash”:

        #!/bin/bash
        STARTFLAG="false"
        while read LINE; do
            if [ "$STARTFLAG" == "true" ]; then
                    if [ "$LINE" == '<!-- this is token 2 -->' ];then
                            exit
                    else
                            echo "$LINE"
                    fi
            elif [ "$LINE" == '<!-- this is token 1 -->' ]; then
                    STARTFLAG="true"
                    continue
            fi
        done < t.txt
        

        亲切的问候

        realex

        【讨论】:

          【解决方案6】:

          对于这样的事情,我会使用 Perl,它结合了(以及其他)sedawk 功能。类似的东西(注意 - 未经测试):

          my $recording = 0;
          my @results = ();
          while (<STDIN>) {
             chomp;
             if (/token 1/) {
                $recording = 1;
             }
             else if (/token 2/) {
                $recording = 0;
             }
             else if ($recording) {
                push @results, $_;
             }
          }
          

          【讨论】:

            【解决方案7】:
            sed -n "/TOKEN1/,/TOKEN2/p" <YOUR INPUT FILE> | sed -e '/TOKEN1/d' -e '/TOKEN2/d'
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-11-13
              • 1970-01-01
              • 1970-01-01
              • 2019-10-22
              • 2015-08-16
              • 1970-01-01
              • 2017-12-13
              相关资源
              最近更新 更多