用awk拆分csv:如何考虑返回?

时间:2015-05-15 10:13:55

标签: awk

我有这个文件:

field1|field2|field3|f41;f42|f5
field1|field2|field3|f41|f5|
field1|field2|field3|f41;f42;f43|f5

我想解析它并获得:

field1|field2|field3|f41|f5
field1|field2|field3|f42|f5
...

简而言之,根据字段4中的半圆进行子分析。 我的awk脚本如下:

awk < myfile.txt -F\| '{ 
n=split($4,a,";");
print $1
for(i=0; ++i <= n;)
print $1"|"$2"|"$3"|"a[i]"|"$5"|"; 
}'

无论如何,对于没有以&#34; |&#34;我得到以下行的第一个字符消失! 例如,鉴于我得到的文件:

field1|field2|field3|f41|f5
ield1|field2|field3|f42|f5

我认为这是因为没有&#34; |&#34;在行尾。 有没有办法告诉awk考虑回车?

2 个答案:

答案 0 :(得分:3)

  1. 不要使用像for(i=0; ++i <= n;)这样的古怪语法来编写你的for循环,因为它只会混淆你的代码(例如,我们需要先考虑i第一次通过该循环是0还是1因为它没有明确说明)。只需将它们写成for (init;condition;increment)for(i=1;i <= n;i++)
  2. 即可
  3. 不要将输入重定向到awk,例如awk < file 'script',只需让awk打开文件awk 'script' file,这样您就可以在脚本中访问FILENAME
  4. 不要在整个脚本中添加虚假的分号 - 这不是C
  5. 不要多次打印硬编码的字段分隔符,例如print $1"|"$2"|"$3"|"a[i]"|"$5,请按设计使用OFS:OFS="|";...;print $1,$2,$3,a[i],$5
  6. 不要在正则表达式上下文中使用字符串,除非您有充分的理由这样做,因为它们只是混淆,复杂化并降低代码的效率,例如:而不是split($4,a,";"),您应该使用split($4,a,/;/)
  7. 使用空白/缩进,它的价格非常便宜。
  8. 所以第1步是重写你的脚本:

    awk < myfile.txt -F\| '{ 
    n=split($4,a,";");
    print $1
    for(i=0; ++i <= n;)
    print $1"|"$2"|"$3"|"a[i]"|"$5"|"; 
    }'
    

    为:

    awk '
    BEGIN { FS=OFS="|" }
    {
        n=split($4,a,/;/)
        print $1
        for(i=1; i<=n; i++)
            print $1, $2, $3, a[i], $5, "" 
    }
    ' myfile.txt
    

    通过修复你的for循环语法,我们现在可以清楚地看到你正在打印第一个字段两次,这是第一次在它自己的行上,所以我们可以立即改变那个:

    $ awk '
    BEGIN { FS=OFS="|" }
    {
        n=split($4,a,/;/)
        for(i=1; i<=n; i++)
            print $1, $2, $3, a[i], $5, ""
    }
    ' myfile.txt
    field1|field2|field3|f41|f5|
    field1|field2|field3|f42|f5|
    field1|field2|field3|f41|f5|
    field1|field2|field3|f41|f5|
    field1|field2|field3|f42|f5|
    field1|field2|field3|f43|f5|
    

    那么 - 你想要的是什么?不幸的是,您对所有输入行上的相同字段位置使用了相同的值,因此我们无法确定哪些输出行/字段来自哪些输入行/字段而您没有发布完整的预期输出,因此我们无法判断以上是否是预期的输出。它还不清楚你是否真的想要总是在每个输出行的末尾打印一个空字段,或者你是否真的想要硬编码输出字段的数量。

    哦,如果你的输出中的字符消失了,因为你的输入文件中有控件-Ms或其他一些虚假的控制字符。使用cat -v查看它们,dos2unix或类似内容,如果它们是控件,则删除它们。

答案 1 :(得分:1)

这对你有用

    awk -F"|" '{n=split($4,a,";"); for(i=1;i<=n;i++){ print $1FS$2FS$3FS a[i] FS $5}}' file.dat
field1|field2|field3|f41|f5
field1|field2|field3|f42|f5
field1|field2|field3|f41|f5
field1|field2|field3|f41|f5
field1|field2|field3|f42|f5
field1|field2|field3|f43|f5