字段以字符串形式包含字段分隔符:在这种情况下如何正确应用awk?

字段以字符串形式包含字段分隔符:在这种情况下如何正确应用awk?,awk,Awk,我有一个类似于test.CSV文件的CSV文件: Header 1; Header 2; Header 3 A;B;US C;D;US E;F;US G;H;FR I;J;FR K;L;FR M;"String with ; semicolon";UK N;"String without semicolon";UK O;"String OK"; P;"String OK"; M;"String with | semicolon";UK N;"String without semicolon";

我有一个类似于test.CSV文件的CSV文件:

Header 1; Header 2; Header 3
A;B;US
C;D;US
E;F;US
G;H;FR
I;J;FR
K;L;FR
M;"String with ; semicolon";UK
N;"String without semicolon";UK
O;"String OK";
P;"String OK";
M;"String with | semicolon";UK
N;"String without semicolon";UK
O;"String OK";
P;"String OK";
现在,我想根据头3分割这个文件。因此,我想以四个单独的CSV文件结束,一个用于“US”、“FR”、“UK”和“”

由于我的Linux命令行技能非常有限(遗憾的是:-)(到目前为止,我一直使用这一行:

awk -F\; 'NR>1{ fname="country_yearly_"$3".csv"; print >>(fname); close(fname);}' test.csv
当然,您有经验的命令行用户会注意到我的问题:my test.csv中的一个字段包含的行中,我用作分隔符的分号也出现在用引号标记的字段中(我不能保证这一点,因为有数百万行,但我很高兴有一个假设这一点的答案)。因此,很遗憾,我得到了一个名为country_yearly_u分号“.csv的附加文件,在我的示例中包含这一行

在解决这个问题的过程中,我在上遇到了这个问题。特别是,Thor的回答似乎包含了通过替换字符串中的所有分号来解决我问题的方法。我相应地调整了他的代码,如下所示:

awk -F'"' -v OFS='' '
  NF > 1 { 
    for(i=2; i<=NF; i+=2) { 
      gsub(";", "|", $i);
      $i = FS $i FS;       # reinsert the quotes
    }
    print
  }' test.csv > test1.csv
正如你所看到的,所有带引号的行都显示出来了,我的问题行也被修复了,但是a)我实际上想要所有的行,而不仅仅是带引号的行,我无法确定他的代码中哪一部分将行限制为带引号的行,b)我认为如果只是更改test.csv而不是将输出发送到新文件,效率会更高,但我也不知道如何做到这一点

根据Birei的回答进行编辑:

不幸的是,我的示例太简单了。下面是一个更新版本:

Header 1; Header 2; Header 3; Header 4
A;B;US; 
C;D;US;
E;F;US;
G;H;FR;
I;J;FR;
K;L;FR;
M;"String with ; semicolon";UK;"Yet another ; string"
N;"String without semicolon";UK; "No problem here"
O;"String OK";;"Fine"
P;"String OK";;"Not ; fine"

请注意,我的真实数据大约有100列和数百万行,忽略字符串中分号的国家列是第13列。然而,据我所知,如果我不先去掉字符串中的分号,我就不能使用第13列这一事实。

您几乎找到了解决方案。我将使用最后一个字段来避免带双引号的f字段。此外,不需要关闭每个文件。它们将由shell在
awk
脚本末尾自动关闭

awk '
    BEGIN {
        FS = OFS = ";";
    }
    FNR > 1 {
        fname = "country_yearly_" $NF ".csv";
        print >>fname;
    }
' infile
检查输出:

head country_yearly_*
这将产生:

==> country_yearly_.csv <==
O;"String OK";
P;"String OK";

==> country_yearly_FR.csv <==
G;H;FR
I;J;FR
K;L;FR

==> country_yearly_UK.csv <==
M;"String with ; semicolon";UK
N;"String without semicolon";UK

==> country_yearly_US.csv <==
A;B;US
C;D;US
E;F;US

==>country\u yearly\u csv country\u yearly\u FR.csv country\u yearly\u UK.csv country\u yearly\u US.csv您几乎找到了解决方案。我会使用最后一个字段来避免双引号字段的问题。此外,不需要关闭每个文件。它们将在
awk
脚本结束时由shell自动关闭

awk '
    BEGIN {
        FS = OFS = ";";
    }
    FNR > 1 {
        fname = "country_yearly_" $NF ".csv";
        print >>fname;
    }
' infile
检查输出:

head country_yearly_*
这将产生:

==> country_yearly_.csv <==
O;"String OK";
P;"String OK";

==> country_yearly_FR.csv <==
G;H;FR
I;J;FR
K;L;FR

==> country_yearly_UK.csv <==
M;"String with ; semicolon";UK
N;"String without semicolon";UK

==> country_yearly_US.csv <==
A;B;US
C;D;US
E;F;US

==>country\u yearly\u.csv country\u yearly\u FR.csv country\u yearly\u UK.csv country\u yearly\u US.csv要拆分文件,您可以执行以下操作:

awk -v FS=";" '{ CSV_FILE = "country_yearly_" $NF ".csv" ; print > CSV_FILE }'
它总是使用最后一个字段来构造文件名

在您的示例中,由于
NF>1
模式,仅打印带引号的行。以下脚本将打印所有行:

awk -F'"' -v OFS='' '
  NF > 1 { 
    for(i=2; i<=NF; i+=2) { 
      gsub(";", "|", $i);
      $i = FS $i FS;       # reinsert the quotes
    }
  }
  {
    # print all lines
    print
  }' test.csv > test1.csv
awk-F''-v of s=''
NF>1{

对于(i=2;i要分割文件,您可以执行以下操作:

awk -v FS=";" '{ CSV_FILE = "country_yearly_" $NF ".csv" ; print > CSV_FILE }'
它总是使用最后一个字段来构造文件名

在您的示例中,由于
NF>1
模式,仅打印带引号的行。以下脚本将打印所有行:

awk -F'"' -v OFS='' '
  NF > 1 { 
    for(i=2; i<=NF; i+=2) { 
      gsub(";", "|", $i);
      $i = FS $i FS;       # reinsert the quotes
    }
  }
  {
    # print all lines
    print
  }' test.csv > test1.csv
awk-F''-v of s=''
NF>1{

对于(i=2;我同意这个答案。不过,不幸的是,我把我的最小示例设置得太简单了:我要拆分的列不是最后一列。更糟糕的是,它位于可以包含字符串的列之间。我将相应地更新我的示例。感谢你的回答。不过,不幸的是,我把我的最小示例设置得太简单了:我要拆分的列lit并不是最后一列。更糟糕的是,它位于可以包含字符串的列之间。我将相应地更新我的示例。哇,这非常有效,甚至我的2GB真实示例(仍然让我兴奋的是,这几行在一分钟内做了如此惊人的事情……。所以肯定+1!下一步是了解您的解决方案;-)哇,这工作得很好,更让我兴奋的是我的2GB真实示例(仍然让我兴奋,那几行在一分钟内做了如此惊人的事情…。所以肯定+1!下一步是了解您的解决方案;-)