【问题标题】:Sed/Awk script to print fields based on first characters基于第一个字符打印字段的 Sed/Awk 脚本
【发布时间】:2014-12-18 23:02:03
【问题描述】:

我是 sed 和 awk 的新手。因此,我不确定该特定任务使用哪一个(或者即使使用正确)。这是我想做的:

我有一个文件,其中包含由“|”分隔的数据行。它看起来像这样:

ln1: |a=1|b=5|d=77|h=2222|
ln2: |c=9|b=21|g=0.00001|a=3|k="helloworld"|h=101|
.....

现在每行可以有不同数量的字段,并且字段可能不会以相同的顺序出现。

我想编写一个脚本来根据字段的 ID 提取字段。所以假设我想要以“a=”和“h=”以及“g=”开头的字段(如果“g=”字段存在),脚本将打印出来

ln1:|a=1|h=2222|
ln2:|a=3|h=101|g=0.00001|

非常感谢大家!

【问题讨论】:

  • 是以ln1: 开头的行吗?它们也是以| 分隔符开头的吗?
  • grep 呢? grep -E "^\|[ahg]=" filename ?
  • 行不以ln1: 开头,它们确实以| 开头,但我可以轻松更改!

标签: awk sed


【解决方案1】:

类似

$ awk -F\| '{out=$1"|"; for ( i=0; i< NF; i++ ) out =out""($i ~ /^[ahg]/?$i"|":""); print out}' input
ln1: |a=1|h=2222|
ln2: |g=0.00001|a=3|h=101|

如果您希望输出按顺序排列,请提供一个冗长的版本a h g

$ awk -F\| '
BEGIN{a[1]="a=[^|]+"; 
      a[2]="h=[^|]+"; 
      a[3]="g=[^|]+" } 

     {out=$1"|"; 
      for(i in a) {
          match($0,a[i] ,arr); 
          out=out""arr[0]"|"
       } 
     print out 
    }' input


ln1: |a=1|h=2222||
ln2: |a=3|h=101|g=0.00001|

【讨论】:

  • 匹配函数中的第二个逗号周围不断出现语法错误。知道为什么吗?我喜欢它如何在输出中打印一个空字段。
  • @AnPocArBuile 我无法重现该错误。您使用的是哪个 awk 版本。测试于GNU Awk 3.1.5
  • @AnPocArBuile 我不确定。但它似乎在我的系统上工作
  • 是否进行了更新,并且运行良好。谢谢你的帮助!! :)
【解决方案2】:

仅使用awk:

awk -vfields_to_write='a,h,g' -F'\|' '{
  for (i=1;i<=NF;i++) {
        split($i,arr,"="); d[arr[1]]=arr[2]; 
  }
  split(fields_to_write,fields,","); 
  str="";
  for (f in fields) 
      if (d[fields[f]]) 
          str=str"|"fields[f]"="d[fields[f]]; 
  print str"|"
 }'

输出将如下所示:

|a=1|h=2222|
|a=3|h=101|g=0.00001|

【讨论】:

  • 如果split() 的任何字段在字符串中包含= 将失败,例如如果k="helloworld"k="hello=world",并且d[fields[f]] 上的测试将失败(如果有)的字段的值为 null 或数字零,例如a=0。此外,in 运算符将按照它们在哈希表中存储的顺序打印字段,而不是在命令行中指定的顺序。
【解决方案3】:
awk -F\| -v ORS=\| '{for(i=1;i<=NF;i++){if(match($i,/ln|a=|h=|g=/)){print $i}}printf("\n")}' File

示例:

AMD$ awk -F\| -v ORS=\| '{for(i=1;i<=NF;i++){if(match($i,/ln|a=|h=|g=/)){print $i}}printf("\n")}' File
ln1: |a=1|h=2222|
ln2: |g=0.00001|a=3|h=101|

【讨论】:

  • 您可以将-v ORS="|" 更改为-v ORS=\| 以使其更等于-F\|
  • 甚至更好的BEGIN{FS=ORS="|"} 这样您就不必对相同的值进行两次硬编码。同样,/ln|a=|h=|g=/ 可以简化为 /ln|[ahg]=/
【解决方案4】:
# put all field ordered, multi instance if wanted as output wanted in place
# of ahg in line here after (ex: babh)
s/^/ahg-/;G
:cycle
s/^\([^-]\)\([^-]*\)-\([^:]*:\)\(.*\)\(|\1=[^|]*\)\(.*\)/\2-\3\4\6\5/
t cycle
s/^[^-]//
t cycle
s/-\([^:]*:\).*\n\(.*\)/\1\2|/
  • 有点长,但不限于 ahg,修改很少
  • 假设始终至少有 1 个可用字段(无错误管理)

posix 版本(所以--posixon GNU sed)

【讨论】:

    【解决方案5】:

    当您有一个包含name=value 对的输入文件时,最好创建一个包含该映射的数组,然后您可以按名称打印所需的字段。例如:

    $ cat tst.awk
    BEGIN{
        FS=" *[|] *"
        OFS="|"
        split(flds,fldsA,"")
    }
    {
        delete n2v          # or split("",n2v) if non-gawk
        for (i=2; i<=NF; i++) {
            name = value = $i
            sub(/=.+/,"",name)
            sub(/[^=]+=/,"",value)
            n2v[name] = value
        }
    
        printf "%s", $1
        for (i=1; i in fldsA; i++) {
            name = fldsA[i]
            if (name in n2v) {
                value = n2v[name]
                printf "%s%s=%s", OFS, name, value
            }
        }
        print "|"
    }
    

    .

    $ awk -v flds="ahg" -f tst.awk file
    ln1:|a=1|h=2222|
    ln2:|a=3|h=101|g=0.00001|
    $
    $ awk -v flds="db" -f tst.awk file
    ln1:|d=77|b=5|
    ln2:|b=21|
    $
    $ awk -v flds="hkba" -f tst.awk file
    ln1:|h=2222|b=5|a=1|
    ln2:|h=101|k="helloworld"|b=21|a=3|
    $
    

    【讨论】:

      猜你喜欢
      • 2018-07-07
      • 1970-01-01
      • 2014-04-07
      • 2022-01-05
      • 1970-01-01
      • 2013-02-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多