【问题标题】:awk - how to replace semicolon in string in csv file?awk - 如何替换csv文件中字符串中的分号?
【发布时间】:2017-03-13 02:18:30
【问题描述】:

我需要在我的公司管理 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

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

【问题讨论】:

  • 由于分号被设置为您的分隔符,awk 毫不奇怪地将其视为分隔符。因此,您的脚本将无法找到要替换的独立分号。您需要一种不同的方法...
  • 不,您的命令不起作用。怎么可能?您将每一行拆分为每个 '; 的字段,然后要求它用 , 替换每个 ; within 每个字段,但 在任何字段中都没有 ;s字段,因为您将输入拆分为;s。脚本如何知道 ; 何时位于字段内而不是字段之间?
  • 我猜是这样的 :) 稍后我会尝试 Johns 的解决方案,看起来不错。

标签: bash csv awk


【解决方案1】:

将第五个及以后的; 替换为,

$ 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&lt;=NF;i++) printf "%s%s",$i,(i==NF?ORS:(i&lt;=4?";":","))

    这将遍历每个字段并打印该字段后跟 (a) ORS 如果我们在最后一个字段上,或 (b) , 如果是在字段 5 或更高版本上,或者 (c) ; 如果我们在前四个领域之一。

将所有; 替换为,

试试:

$ 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\;

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

  • $1=$1

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

  • 1

    这告诉 awk 打印该行。

  • OFS=,

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

备选方案#1

$ 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

备选方案#2

$ sed 's/;/,/g'  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

【讨论】:

  • 我认为这不能回答 OP 问题。他要求替换第五个分号后的分号,而不是用作分隔符。
  • @karakfa 有趣。我在关注 OP 的文本,而不是示例代码。 OP 没有提供所需的输出。也许你是对的。 OP需要澄清。
  • @karakfa 我刚刚添加了一个方法来仅替换第五个或更晚的;。
【解决方案2】:

如果有问题的分号仅出现在您的第 5 个字段中,那么您可以使用 GNU awk 来执行此操作,以匹配()的第 3 个参数:

$ 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】:

    我认为您的问题是在五字段宽的输入中替换逻辑第四字段中的非引号分隔符。虽然这个脚本是重复的,但应该更容易理解

    $ 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 的 cmets 编写相同内容的更好方法

    $ 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
    

    【讨论】:

      【解决方案4】:

      如果应该删除您的第五个;,请将 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
      

      【讨论】:

        猜你喜欢
        • 2021-06-25
        • 2018-08-06
        • 2018-09-11
        • 1970-01-01
        • 2017-05-20
        • 2014-11-20
        • 1970-01-01
        • 2015-04-06
        • 1970-01-01
        相关资源
        最近更新 更多