awk - 如何在csv文件中替换字符串中的分号?

时间:2016-10-30 23:21:27

标签: bash csv awk

我需要管理我公司的smtp日志文件处理。

这些日志文件需要导入到MSSQL中,因此我的工作就是提供这些数据。

我收到了奇怪的未送达消息“;”在字符串中,我需要用逗号替换它。

所以我得到了:

Sender;Recipient;Operation;Answer;Error;Servername
bla@bla.com;rockit@sohard.com;RCPT TO;450;+4.2.0+<rockit@sohard.com>:+Recipient+address+rejected:+Policy+restrictions;+try+later;M0641

提到“;”在“限制”之后的答案字段中,不知道为什么邮件服务器发送分号,也许是为了惹恼我:P

在我做了大量研究后,我尝试用awk跟进:

awk 'BEGIN{FS=OFS=";"} {for (i=5;i<=NF;i++) gsub (";",",",$i)} 1' myfile.csv

这个命令实际上有效,但似乎它对我的文件没有任何作用,“;”在错误字段中仍然存在。我在这里缺少什么?

4 个答案:

答案 0 :(得分:2)

;

替换第五个及以后的,
$ awk -F\; '{for (i=1;i<=NF;i++) printf "%s%s",$i,(i==NF?ORS:(i<=4?";":","))}' myfile.csv 
Sender;Recipient;Operation;Answer;Error,Servername
bla@bla.com;rockit@sohard.com;RCPT TO;450;+4.2.0+<rockit@sohard.com>:+Recipient+address+rejected:+Policy+restrictions,+try+later,M0641

工作原理:

  • -F\;

    这会将输入的字段分隔符设置为;

  • for (i=1;i<=NF;i++) printf "%s%s",$i,(i==NF?ORS:(i<=4?";":","))

    这会遍历每个字段并打印字段,然后按(a)ORS(如果我们在最后一个字段上),或者(b),如果是在字段5或更高字段,或(c){{ 1}}如果我们在前四个字段之一。

;

替换所有;

尝试:

,

工作原理:

  • $ awk -F\; '{$1=$1} 1' OFS=, myfile.csv Sender,Recipient,Operation,Answer,Error,Servername bla@bla.com,rockit@sohard.com,RCPT TO,450,+4.2.0+<rockit@sohard.com>:+Recipient+address+rejected:+Policy+restrictions,+try+later,M0641

    这会将输入的字段分隔符设置为分号。

  • -F\;

    这会导致awk认为该行已被更改,因此awk将更新输出行以使用新的字段分隔符。

  • $1=$1

    这告诉awk打印该行。

  • 1

    这会将输出上的字段分隔符设置为逗号。

备选方案#1

OFS=,

备选方案#2

$ awk '{gsub(/;/, ",")} 1' myfile.csv
Sender,Recipient,Operation,Answer,Error,Servername
bla@bla.com,rockit@sohard.com,RCPT TO,450,+4.2.0+<rockit@sohard.com>:+Recipient+address+rejected:+Policy+restrictions,+try+later,M0641

答案 1 :(得分:1)

我认为你的问题是在五个字段范围的输入中替换逻辑第四个字段中的unquotes分隔符。虽然这个重复的脚本应该更容易理解

$ awk '{n=split($0,a,";"); 
        for(i=1; i<4; i++)   printf "%s;", a[i]; 
        for(i=4; i<n-1; i++) printf "%s,", a[i]; 
        printf "%s;%s\n", a[n-1], a[n]}' file 

根据@Ed Morton的评论写一个更好的方法

$ awk -F';' '{for(i=1; i<NF-1; i++) printf "%s"(i<4?FS:","), $i; 
              print $(NF-1) FS $NF}' file

输入

1;2;3;4a;4b;4c;5
1;2;3;4;5

它会生成

1;2;3;4a,4b,4c;5
1;2;3;4;5

答案 2 :(得分:0)

如果违规的分号只出现在你的第5个字段中,那么你可以使用GNU awk为第3个arg匹配():

$ awk 'match($0,/(([^;]+;){4})(.*)(;[^;]+$)/,a){gsub(/;/,",",a[3]); print a[1] a[3] a[4]}' file
bla@bla.com;rockit@sohard.com;RCPT TO;450;+4.2.0+<rockit@sohard.com>:+Recipient+address+rejected:+Policy+restrictions,+try+later;M0641

答案 3 :(得分:0)

如果您的第五个;应该被删除,请将$ 6追加到$ 5并相应提前。这可以通过for循环完成(SO中有例子),但由于错误接近结尾,我们只是以更简单的方式做到这一点:

$ awk 'BEGIN {FS=OFS=";"} NR==1 {nf=NF} NF==(nf+1) {$5=$5 "," $6; $6=$7; NF=nf} 1' file

说明:

BEGIN {FS=OFS=";"}   # set separator
NR==1 {nf=NF}        # get field count from the first record (6)
NF==(nf+1) {         # if record is one field longer:
    $5=$5 "," $6     # append $6 to $5, comma-separated
    $6=$7            # set $7 (NF) to $6 (nf)
    NF=nf            # reset NF
} 1                  # output

测试:运行程序并将输出发送到cut -d\; -f 5输出:

Error
+4.2.0+<rockit@sohard.com>:+Recipient+address+rejected:+Policy+restrictions,+try+later