【问题标题】:Extract text between two strings on different lines在不同行的两个字符串之间提取文本
【发布时间】:2016-10-27 04:14:48
【问题描述】:

我有一个包含以下随机主机的大电子邮件文件:

......
HOSTS: test-host,host2.domain.com,
host3.domain.com,another-testing-host,host.domain.
com,host.anotherdomain.net,host2.anotherdomain.net,
another-local-host, TEST-HOST

DATE: August 11 2015 9:00
.......

主机总是用逗号分隔,但它们可以分成一行、两行或多行(我无法控制这一点,很遗憾,这是电子邮件客户端所做的)。

所以我需要提取字符串“HOSTS:”和字符串“DATE:”之间的所有文本,将其换行,并用新行替换逗号,如下所示:

test-host
host2.domain.com
host3.domain.com
another-testing-host
host.domain.com
host.anotherdomain.net
host2.anotherdomain.net
another-local-host
TEST-HOST

到目前为止,我想出了这个,但我失去了与“HOSTS”同一行的所有内容:

sed '/HOST/,/DATE/!d;//d' ${file} | tr -d '\n' | sed -E "s/,\s*/\n/g"

【问题讨论】:

  • 您的错误是 // 不像您(我认为)假设的那样只匹配空行。请改用/^$/d/./!d。你最终会得到比你想要的更多的文本,但我认为你可以从那里得到它......

标签: bash awk sed


【解决方案1】:

这样的事情可能对你有用:

sed -n '/HOSTS:/{:a;N;/DATE/!ba;s/[[:space:]]//g;s/,/\n/g;s/.*HOSTS:\|DATE.*//g;p}' "$file"

细分:

-n                       # Disable printing
/HOSTS:/ {               # Match line containing literal HOSTS:
  :a;                    # Label used for branching (goto)
  N;                     # Added next line to pattern space
  /DATE/!ba              # As long as literal DATE is not matched goto :a
  s/.*HOSTS:\|DATE.*//g; # Remove everything in front of and including literal HOSTS:
                         # and remove everything behind and including literal DATE 
  s/[[:space:]]//g;      # Replace spaces and newlines with nothing
  s/,/\n/g;              # Replace comma with newline
  p                      # Print pattern space
}

【讨论】:

  • 我将此标记为正确答案。我测试了它,它有效,我也喜欢这个解释,但我会用我自己的方法,感谢 Jeff Y 的建议,它现在有效。
【解决方案2】:

这个 awk 单行代码可能会有所帮助:

awk -v RS='HOSTS: *|DATE:' 'NR==2{gsub(/\n/,"");gsub(/,/,"\n");print}' input

【讨论】:

  • 由于多字符 RS,您应该提到它是 gawk 特定的
【解决方案3】:

另一个awktr

$ awk '/^HOSTS:/{$1="";p=1} /^DATE:/{p=0} p' file | tr -d ' \n' | tr ',' '\n'; echo ""

test-host
host2.domain.com
host3.domain.com
another-testing-host
host.domain.com
host.anotherdomain.net
host2.anotherdomain.net
another-local-host
TEST-HOST

【讨论】:

    【解决方案4】:

    这是另一个可能对您有用的 sed 脚本:

    script.sed

    /HOSTS:/,/DATE/ { 
        /DATE/! H;                        # append to HOLD space
        /DATE/ { g;                       # exchange HOLD and PATTERN space
                 s/([\n ])|(HOSTS:)//g;   # remove unwanted strings
                 s/,/\n/g;                # replace comma with newline
                 p;                       # print
        }
    }
    

    这样使用:sed -nrf script.sed yourfile

    中间块应用于HOSTS:DATE 之间的行。在中间块中,不匹配 DATE 的行被附加到 Hold-Space 中,匹配 DATE 的行触发更长的操作。

    【讨论】:

      【解决方案5】:

      Perl 来救援!

      perl -ne '
          if (my $l = (/^HOSTS:/ .. /^DATE:/)) {
              chomp;
              s/^HOSTS:\s+// if 1 == $l;
              s/DATE:.*// if $l =~ /E/;
              s/,\s*/\n/g;
              print;
          }' input-file > output-file
      

      触发器运算符.. 返回一个数字,在这种情况下表示当前块中的行号。因此,我们可以轻松地从第一行 (1 == $l) 中删除 HOSTS:。最后一行可以通过数字后面的E0来识别,这就是我们删除DATE:...的方法

      【讨论】:

        【解决方案6】:
        cat ${file} | awk 'BEGIN {A=0;} /^HOST/ {A=1;} /^DATE/ {A=0} {if (A==1) print;}' | tr -d '\n' | sed -E "s/,\s*/\n/g" | sed -e 's/^HOSTS\s*://\s*//
        

        【讨论】:

        • UUOC。总是引用你的 shell 变量。不要使用所有大写的 var 名称。 awk 永远不需要 sed 等。 {if (A==1) print;} 可以简单地写成A。您不需要 awk 中的虚假分号。始终在脚本周围使用单引号(例如 sed),而不是双引号。 \s 是 GNU sed 特定的,所以你应该说明这一点。
        【解决方案7】:
        awk 'sub(/^HOSTS: /,""){rec=""} /^DATE/{gsub(/ *, */,"\n",rec); print rec; exit} {rec = rec $0}' file
        test-host
        host2.domain.com
        host3.domain.com
        another-testing-host
        host.domain.com
        host.anotherdomain.net
        host2.anotherdomain.net
        another-local-host
        TEST-HOST
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-05-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-13
          • 2013-05-14
          相关资源
          最近更新 更多