Bash 从可变长度换行输入创建csv

Bash 从可变长度换行输入创建csv,bash,awk,Bash,Awk,看起来很简单,但我已经玩了一段时间,找不到优雅的东西 所以我有这样的数据: Field1 09:30 Field2 H Field3 Happy Field1 09:35 Field3 Sad Field1 09:40 Field2 C Field1 09:45 Field2 P Field3 Pleased awk -f csv.awk input.txt 。。。基本上,字段1将始终存在,其他字段是可选的。我想将其拆分为csv(遗憾的是我不能使用python),以便适当地保留空格 09:3

看起来很简单,但我已经玩了一段时间,找不到优雅的东西

所以我有这样的数据:

Field1 09:30
Field2 H
Field3 Happy
Field1 09:35
Field3 Sad
Field1 09:40
Field2 C
Field1 09:45
Field2 P
Field3 Pleased
awk -f csv.awk input.txt
。。。基本上,字段1将始终存在,其他字段是可选的。我想将其拆分为csv(遗憾的是我不能使用python),以便适当地保留空格

09:30, H, Happy
09:35, , Sad
09:40, C, 
09:45, P, Pleased 
让我们试试这个:

awk 'BEGIN{OFS=", "}
     p && /Field1/
         {    print a["Field1"], a["Field2"], a["Field3"];
              a["Field1"]=a["Field2"]=a["Field3"]=""
         }
     {a[$1]=$2; p=1}
     END{print a["Field1"], a["Field2"], a["Field3"]}
    ' file
它返回:

$ awk 'BEGIN{OFS=", "} p && /Field1/ {print a["Field1"], a["Field2"], a["Field3"]; a["Field1"]=a["Field2"]=a["Field3"]=""} {a[$1]=$2; p=1} END{print a["Field1"], a["Field2"], a["Field3"]}' file
09:30, H, Happy
09:35, , Sad
09:40, C, 
09:45, P, Pleased
解释
  • BEGIN{OFS=“,”}
    将输出字段分隔符设置为
    (逗号,空格)
  • p&&/Field1/{}
    如果
    p
    标志为“开”,且该行包含
    Field1
    ,则执行
    {}
  • 打印一个[“Field1”]、一个[“Field2”]、一个[“Field3”]
    打印
    a[]
    数组的三个值
  • a[“Field1”]=a[“Field2”]=a[“Field3”]=”
    清空数组
  • {a[$1]=$2;p=1}
    对于每一行,将第二列值存储在
    a[]
    数组中。另外,激活
    p
    标志,以便在找到下一个
    字段1
    时开始打印行
  • END{print a[“Field1”]、a[“Field2”]、a[“Field3”}
    打印最后一段数据

    • 这里有一个与
      awk
      相关的替代方案:

      # new records always starting with Field1
      /Field1/ {
        # print record if it isset
        if(length(r[0])>0) {
          printf "%s, %s, %s\n", r[0], r[1], r[2]
        };  
      
        # reinitialize record
        r[0]=r[1]=r[2]=""
      
        # copy value
        r[0]=$2
      }
      
      /Field2/ {
        # copy value
        r[1]=$2
      }
      
      /Field3/ {
        # copy value
        r[2]=$2
      }
      
      # the END block idea comes from @fedorqui. Thanks!
      END {
        # print record if it isset
        if(length(r[0])>0) {
          printf "%s, %s, %s\n", r[0], r[1], r[2]
        };  
      }
      
      将脚本保存在
      csv.awk
      中。然后像这样执行
      awk

      Field1 09:30
      Field2 H
      Field3 Happy
      Field1 09:35
      Field3 Sad
      Field1 09:40
      Field2 C
      Field1 09:45
      Field2 P
      Field3 Pleased
      
      awk -f csv.awk input.txt
      

      下面是
      perl
      中的一个解决方案:

      perl -lane 'if(/Field([\d])/){
                     if($1==1 && $.!=1)
                       {
                        print join ",",@a;
                        undef @a 
                       }
                       $a[$1-1]=$F[1]}
                   END{print join ",",@a}' your_file
      
      /Field([\d])/
      -[\d]周围的大括号将捕获$1中的数字,以后可以将其用作数组索引

      $a[$1-1]=$F[1]->
      将行的第二个字段存储在前面捕获的索引处

      if($1==1 && $.!=1)
                       {
                        print join ",",@a;
                        undef @a 
                       }# 
      
      捕获的十进制数为1时立即打印数组,并清空数组

      最后会留下一个数组,该数组将在
      end
      块中打印

      测试如下:

      > cat temp
      Field1 09:30
      Field2 H
      Field3 Happy
      Field1 09:35
      Field3 Sad
      Field1 09:40
      Field2 C
      Field1 09:45
      Field2 P
      Field3 Pleased
      > perl -lane 'if(/Field([\d])/){if($1==1 && $.!=1){print join ",",@a;undef @a }$a[$1-1]=$F[1]}END{print join ",",@a}' temp
      09:30,H,Happy
      09:35,,Sad
      09:40,C
      09:45,P,Pleased
      >
      

      看来你和我一样厌倦了同样的数据格式化工作。
      下面是我经常使用的代码片段,稍加修改。您可以在第2行中定义任意数量的字段(不一定是相同的模式),然后发出
      awk-f so.awk input.txt
      (假设您将此awk片段保存在so.awk中,并且输入文件为input.txt)

      享受:)

      再来一个

      function dump() { if (a[1]!="") print a[1],a[2],a[3]; a[2]=a[3]=""; }; 
      BEGIN { RS="Field"; OFS=", "; a[1]=a[2]=a[3]="" } 
      END {dump()}
      { if ($1=="1") dump(); a[$1]=$2; }
      

      对必须添加
      END
      块来打印最后的信息:)找不到更好的方法!是的,只需捕获小数点后字段并将其用作数组中的索引
      $a[$1-1]=$F[1]
      。简单!不是吗?我不会说
      awk
      不简单。确实如此,只是数据的获取有点长。简言之:)谢谢,伙计,这很有效。但是有两个问题!首先,当我把它放到bash脚本中时,它的行为会有所不同,你知道为什么吗?我还尝试将其改编为我的真实脚本(实际上有四个字段),但无法获得正确的输出out@mbbxedh2嗯,请确保正确复制和粘贴它。您还可以将其保存在
      script.awk
      中,并使用
      awk-f script.awk运行您的_文件
      。对于实际情况,需要考虑清空所有4个字段。试着发布一些相关的信息(以防你不想发布真实的数据),这样我就能猜出哪里出了问题。成功!