【问题标题】:How include second awk to avoid awk | awk如何包含第二个 awk 以避免 awk | awk
【发布时间】:2020-12-08 10:08:14
【问题描述】:

这是输入字符串:

status=N&Cau=1&Litrs=0&Freq=0&Min=0

这是提取值并解析为 csv 的 awk:(可以工作,但可能会更好..)

awk 'BEGIN {FS="&"}{for (i=1;i<=NF;i++) print $i}' | awk 'BEGIN {FS="="} {printf("%s;"),$2}'

结果是:

N;1;0;0;0;

问题是:有没有办法在一个 awk 中做到这一点? (不是 awk | awk)

CSV 中的第一行将是:

awk 'BEGIN {FS="&"}{for (i=1;i<=NF;i++) print $i}' | awk 'BEGIN {FS="="} {printf("%s;"),$1}'
status;Cau;Litrs;Freq;Min;

【问题讨论】:

    标签: bash awk


    【解决方案1】:

    这是一个设置OFSFS,然后使用$1=$1 trick 重构$0,最后进行一些按摩以将第一个; 移动到最后。

    awk 'BEGIN { OFS=";"; FS="(^|&)[^&]*=" } { $1=$1; print substr($0,2)";" }'
    N;1;0;0;0;
    

    【讨论】:

      【解决方案2】:

      我的意思是……你可以这样做:

      awk -F'&' '
      {
        for (i=1; i<=NF; i++) {
          split($i, x, "=")
          values[i]=x[2]
        }
        
        for (i=1; i<=length(values); i++) {
          printf "%s;", values[i]
        }
      }
      '
      

      但我不知道这是否有很大的改进!

      【讨论】:

        【解决方案3】:

        如果您的输入只有一行 - 看起来可能是因为您正在解析一个 URL - 您可以使用这个:

        awk 'BEGIN {FS="="; RS="&"} {sub(/\n$/,"",$2); printf "%s;", $2} END {printf "\n";}'
        

        这识别出输入只是以不同于换行符和空格的方式分隔的记录和字段 - 因此通过将字段分隔符更改为 = 并将记录分隔符更改为 &amp;,每个 xx=N 段都会被处理好像它是一个单独的行,$2 等于 N。因此,我们只需为每条记录打印$2,后跟分号。但是,如果您在输入行的末尾有一个换行符,您可能会这样做,这会弄乱最后一条记录,这就是需要 sub(/\n$/,"",$2); 的原因,请删除任何换行符。 END 子句提供一个结束换行符。

        【讨论】:

          【解决方案4】:

          听起来想要的结果是一组 2 行输出:

          status;Cau;Litrs;Freq;Min;             # header record
          N;1;0;0;0;                             # data record
          

          使用单个 awk 调用的一个想法(替换当前的 4x awk 调用集):

          indat="status=N&Cau=1&Litrs=0&Freq=0&Min=0"    # assuming OP has data in a variable
          
          awk -F'[&=]' '                                 # define "&" and "=" as input field separators
               { for (i=1 ; i<NF ; i=i+2)                # loop through the odd numbers
                     { hdr=hdr $(i) ";"                  # build header record from odd # fields
                       dat=dat $(i+1) ";"                # build data record from even # fields
                     }
               }
          END  { printf "%s\n%s\n", hdr, dat }           # dump our 2 records to stdout
          ' <<< "${indat}"                               # feed input variable via a here string
          

          这会生成:

          status;Cau;Litrs;Freq;Min;
          N;1;0;0;0;
          

          如果(最终)意图是构建一个包含多行数据的 CSV 文件(例如,输入将来自已解析的 URL 流),那么构建/打印 hdr 变量应该不会太难当FNR==1;此外,我们希望 a) 在 for 循环之前清除 dat 和 b) 在 for 循环之后打印 dat;等等等等等等……

          【讨论】:

            【解决方案5】:

            我略微增强了 jas 提出的解决方案。现在语法将在前面自行解析分隔符,而后面没有分隔符。它也不需要使用 gensub(),因此它完全兼容 mawk(或任何 sub/gsub)

            尽管所有这些正则表达式看起来都需要对所有 4 个主要变量进行显式声明,但所有处理都已经在此处完成,再加上 $1=$1 技巧。

            mawk 'BEGIN { FS = "&[^=]+="; OFS = ";"; RS = "^[^=]+=|\n"; ORS = ";\n"; } ( $1 = $1 )'
            

            【讨论】:

              【解决方案6】:

              至于 2-line 解决方案,它是技巧的组合:

              mawk 'BEGIN { OFS = ";"; FS = "=[^&]*(&|$)"; } 
                          { save0 = $0; sub(/^[^=]+=/, "", save0); } 
                          { $1 = $1; print; }
                          { FS = "&[^=]+="; $0 = save0; } (NF++)'
              

              (从技术上讲,没有必要将它们分成 { } 组,我只是这样编写它以便于阅读)

              为了使其对多行友好,请在其末尾更新 FS,例如

              mawk 'BEGIN { OFS = ";"; FS = "=[^&]*(&|$)"; } 
                          { save0 = $0; sub(/^[^=]+=/, "", save0); } 
                          { $1 = $1; print; }
                          { FS = "&[^=]+="; $0 = save0; FS="=[^&]*(&|$)"; } (NF++)'
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2022-06-28
                • 1970-01-01
                • 2018-10-11
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多