【问题标题】:Extract multiple strings with awk用awk提取多个字符串
【发布时间】:2020-02-11 22:25:18
【问题描述】:

我试图从每一行中提取两个字符串,有些行没有任何一个字符串,有些行只有一个字符串。

我的输入 input.txt:

ID=MD001;refer=init;loc=tap2
ID=MD002;Name=Jam;refer=init;loc=tap2
ID=MD003;Name=Jane;Value=vip;refer=init;loc=tap2
ID=MD008;Name=George;product=car;vall=some;Value=vim;refer=init;loc=tap2
ID=MD0010;product=cars;Value=vip4;refer=init;loc=tap2
ID=MD0018;product=cars;
...

我想匹配字符串“名称”或/和“值”,并将它们输出为:

ID=MD002 Name=Jam
ID=MD003 Name=Jane Value=vip
ID=MD008 Name=George Value=vim
ID=MD0010 Value=vip4

我试过了:

head input.txt | awk '$1 ~ /Name|Value/ {match($1, /(ID=*);.*(Name*);.*(Value.*)/, name); print name[1] name[2] name[3]}'

但它确实打印了任何东西。 感谢您的帮助。

【问题讨论】:

标签: awk match


【解决方案1】:

请您尝试以下操作。

awk '
BEGIN{
  FS=";"
}  
  {
  for(i=2;i<=NF;i++){
    if($i~/^Name|^Value/){
      val=(val?val OFS:"")$i
    }
  }
  if(val){
    print $1,val;
  }
  val=""
}
'  Input_file

所示样本的输出如下。

ID=MD002 Name=Jam
ID=MD003 Name=Jane Value=vip
ID=MD008 Name=George Value=vim
ID=MD0010 Value=vip4

【讨论】:

    【解决方案2】:

    从这里开始:

    $ cat tst.awk
    BEGIN { FS="[=;]" }
    {
        delete f
        for (i=1; i<NF; i+=2) {
            f[$i] = $i "=" $(i+1)
        }
        print f["ID"], f["Name"], f["Value"]
    }
    
    $ awk -f tst.awk file
    ID=MD001
    ID=MD002 Name=Jam
    ID=MD003 Name=Jane Value=vip
    ID=MD008 Name=George Value=vim
    ID=MD0010  Value=vip4
    ID=MD0018
    

    然后调整以适应(使用in 运算符来测试是否存在)如果您不想打印行和/或不打印缺失值:

    $ cat tst.awk
    BEGIN { FS="[=;]" }
    {
        delete f
        for (i=1; i<NF; i+=2) {
            f[$i] = $i "=" $(i+1)
        }
    
        hit = 0
        out = f["ID"]
        out = out val("Name")
        out = out val("Value")
    }
    hit { print out }
    
    function val(tag) {
        if (tag in f) {
            hit = 1
            return (OFS f[tag])
        }
    }
    
    $ awk -f tst.awk file
    ID=MD002 Name=Jam
    ID=MD003 Name=Jane Value=vip
    ID=MD008 Name=George Value=vim
    ID=MD0010 Value=vip4
    

    【讨论】:

    • 不客气。请注意,您还可以重新排列列以按照您想要的任何顺序输出它们,您不必按照与输入相同的顺序输出它们。
    • 是的,通过更改 f[""] 中的列名,您可以打印出您需要的列。晚餐!谢谢埃德
    【解决方案3】:

    另一个类似的awk

    $ awk -F\; -v p='(Name|Value)=' '
           $0~p {printf "%s ", $1; 
                 for(i=2; i<=NF; i++) if($i~p) printf "%s ", $i; 
                 print ""}' file
    
    ID=MD002 Name=Jam
    ID=MD003 Name=Jane Value=vip
    ID=MD008 Name=George Value=vim
    ID=MD0010 Value=vip4
    

    【讨论】:

      【解决方案4】:

      还有一个:

      $ awk -F\; '{                                 # set the delim
          for(i=2;i<=NF;i++)                        # looping from the 2nd field
              if($i~/^(Name|Value)/) {              # looking for keywords and if found
                 for(i=1;i<=NF;i++)                 # restart loop from beginning
                     if($i~/^(Name|Value)/||i==1)   # outputing first field and matches
                         printf "%s ",$i
                 print ""                           # newline in the end
                 # next                             # breaking from the 1st loop not needed
              }
      }' file
      

      输出:

      ID=MD002 Name=Jam 
      ID=MD003 Name=Jane Value=vip 
      ID=MD008 Name=George Value=vim 
      ID=MD0010 Value=vip4 
      

      【讨论】:

        【解决方案5】:

        这可能非常适合 awk 的 FPAT 功能,其中字段由它们的内容而不是它们的分隔符定义。见https://www.gnu.org/software/gawk/manual/html_node/Splitting-By-Content.html

        awk '
            BEGIN {
                    FPAT = "(ID=[^;\v]+)|(Name=[^;\v]+)|(Value=[^;\v]+)"
            }
        
            NF>1 {
                    # Only prints lines that have Name and/or Value
                    for (i=1; i<NF; i++) {
                            printf $i " "
                    }
                    print $NF
            }
        ' file
        

        FPAT 定义了 3 个可能的字段。第一个,(ID=[^;\v]+),意味着它必须以 ID= 开头,并且后跟至少一个既不是分号也不是垂直空格的字符。接下来的两个 (Name=[^;\v]+)(Value=[^;\v]+) 同样表示 Name= 或 Value= 后跟至少一个既不是分号也不是垂直空格的字符。

        【讨论】:

          猜你喜欢
          • 2013-04-09
          • 2015-05-10
          • 2018-12-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多