仅针对特定部分从配置文件中打印行

时间:2018-11-02 20:49:45

标签: regex bash awk sed pcre

我有许多部分的配置文件。我需要收集特定部分的所有行。该部分可能在一个文件中出现几次。 例如:

serviceA:
   ports:
     8080
     1323
serviceB:
  test:
      MMMM
  ports:
     8081
     3123
  network:
    ddddd

我阅读了https://www.shellhacks.com/sed-awk-print-lines-between-two-patterns/这篇文章并开始。

本节开头的模式很简单,它是/ports:/,但是本节末尾的模式不是那么简单,可以是任何名称,例如[a-zA-Z]+:

我尝试在awk和sed中使用模式[a-zA-Z]+:

awk。它仅打印部分名称

awk '/ports:/,/[a-zA-Z]+:/' file
    ports:
    ports:

在sed中。它从第一个端口打印到文件结尾

sed -n '/ports:/,/[a-zA-Z]+:/p' file
ports:
         8080
         1323
    serviceB:
      test:
         MMMM
      ports:
         8081
         3123
      network:
        ddddd

我认为问题在于模式[a-zA-Z]+:与port:匹配,并决定排除port:。  ^\s*((?!ports)[a-zA-Z]+:)+ 这种模式在在线正则表达式测试器中可以正常使用-https://regex101.com/

awk从第一个端口打印到文件结尾

awk '/ports:/,/^\s*((?!ports)[a-zA-Z]+:)+/' file
ports:
             8080
             1323
        serviceB:
          test:
             MMMM
          ports:
             8081
             3123
          network:
            ddddd

目前,我只发现一种情况

awk '/ports:/,/network:|serviceB:/' 
  ports:
      8080
      1323
serviceB:
  ports:
      8081
      3123
network:

但是,我不知道各节的所有可能名称。 我需要一个通用的解决方案。

3 个答案:

答案 0 :(得分:1)

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

sed '/:$/h;//d;G;/ports:$/P;d' file

将每个节名称存储在保留空间中,然后删除该行。对于每隔一行,请附加最新的节名称,如果恰好是ports:,则仅打印当前行。

答案 1 :(得分:0)

根据您的数据格式,类似的东西应该起作用

$ awk '/^[^ ]/{s=$0} /:/{p=0} /ports:/{print s; p=1} p' file

serviceA:
   ports:
     8080
     1323
serviceB:
  ports:
     8081
     3123

捕获服务名称,如果port:与打印服务名称和部分匹配,请在下一个子部分或部分中重置打印标志p

如果不需要节名

$ awk '/:/{p=0} p; /ports:/{p=1}' file

     8080
     1323
     8081
     3123

答案 2 :(得分:0)

您的输入为YAML,最好使用YAML解析器来解析此数据。

您可以使用此ruby代码打印ports,该代码位于所有顶部的第二级:

ruby -ryaml -e "data=YAML::load(STDIN.read); data.each{|n|puts n.last['ports']}" < file.yml

8080 1323
8081 3123