如何让awk忽略双引号内的字段分隔符?

时间:2015-04-15 05:17:53

标签: bash shell awk

我需要在逗号分隔值文件中删除2列。 请考虑csv文件中的以下行:

"abc@xyz.com,www.example.com",field2,field3,field4
"def@xyz.com",field2,field3,field4

现在,我想要的结果是:

"abc@xyz.com,www.example.com",field4
"def@xyz.com",field4

我使用了以下命令:

awk 'BEGIN{FS=OFS=","}{print $1,$4}'

但是引号内的嵌入式逗号会产生问题,以下是我得到的结果:

"abc@xyz.com,field3
"def@xyz.com",field4

现在我的问题是如何让awk忽略双引号内的“?”?

4 个答案:

答案 0 :(得分:35)

从GNU awk手册(http://www.gnu.org/software/gawk/manual/gawk.html#Splitting-By-Content):

$ awk -vFPAT='([^,]*)|("[^"]+")' -vOFS=, '{print $1,$4}' file
"abc@xyz.com,www.example.com",field4
"def@xyz.com",field4

并查看What's the most robust way to efficiently parse CSV using awk?以更一般地解析字段中包含换行符等的CSV。

答案 1 :(得分:10)

这不是bash / awk解决方案,但我建议使用CSVKitpip install csvkit可以安装csvcut --columns=1,4 <<EOF "abc@xyz.com,www.example.com",field2,field3,field4 "def@xyz.com",field2,field3,field4 EOF 。它提供了一系列专门用于CSV的命令行工具,包括csvcut,它完全符合您的要求:

"abc@xyz.com,www.example.com",field4
def@xyz.com,field4

输出:

{{1}}

它删除了不必要的引号,我认为这不应该是一个问题。

阅读CSVKit here on RTD的文档。 ThoughtBot有一个nice little blog post介绍了这个工具,这是我学习CSVKit的地方。

答案 2 :(得分:4)

在您的示例输入文件中,它是引用的第一个字段,只有第一个字段。如果这一般是正确的,那么请考虑以下作为删除第二和第三列的方法:

$ awk -F, '{for (i=1;i<=NF;i++){printf "%s%s",(i>1)?",":"",$i; if ($i ~ /"$/)i=i+2};print""}' file
"abc@xyz.com,www.example.com",field4
"def@xyz.com",field4

正如评论中所提到的,awk本身并不理解引用的分隔符。此解决方案通过查找以引号结尾的第一个字段来解决此问题。然后它会跳过后面的两个字段。

详情

  • for (i=1;i<=NF;i++)

    这会在每个字段for上开始i

  • printf "%s%s",(i>1)?",":"",$i

    这会打印字段i。如果它不是第一个字段,则该字段前面有逗号。

  • if ($i ~ /"$/)i=i+2

    如果当前字段以双引号结束,则会将字段计数器增加2.这就是我们跳过字段2和3的方式。

  • print""

    完成for循环后,会打印换行符。

答案 3 :(得分:1)

无论引用字段在哪里,此awk都应该有效,并且也适用于转义引号。

awk '{while(match($0,/"[^"]+",|([^,]+(,|$))/,a)){
      $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
      print b[1] b[4];x=0}' file

输入

"abc@xyz.com,www.example.com",field2,field3,field4  
"def@xyz.com",field2,field3,field4  
field1,"abc@xyz.com,www.example.com",field3,field4  

输出

"abc@xyz.com,www.example.com",field4
"def@xyz.com",field4
field1,field4

它甚至适用于

field1,"field,2","but this field has ""escaped"\" quotes",field4

强大的FPAT变量失败了!


解释

 while(match($0,/"[^"]+",|([^,]+(,|$))/,a))

只要匹配成功(即有一个字段),就会启动一个while循环 匹配匹配正则表达式的第一次出现,它偶然匹配字段并将其存储在数组a

 $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]

$0设置为从匹配字段的末尾开始,并将匹配的字段添加到b中的相应数组位置。

  print b[1] b[4];x=0}

b打印所需的字段,并为下一行将x设置为零。


缺陷

如果字段包含转义引号和逗号

,则会失败

修改

已更新以支持空字段

awk '{while(match($0,/("[^"]+",|[^,]*,|([^,]+$))/,a)){
     $0=substr($0,RSTART+RLENGTH);b[++x]=a[0]}
     print b[1] b[4];x=0}' file