使用SED在CSV文件的第5个字段之后添加值,该文件也是IP地址

时间:2017-02-16 22:01:19

标签: csv sed

我有一个CSV文件,我正在使用sed进行操作。我正在做的是将当前YYYY-MM-DD HH:MM:SS插入IP地址后的第5个字段。如下所示,每个值都用双引号括起来,每个CSV列用逗号分隔。

"12345","","","None","192.168.2.1","qqq","000"
"67890","ABC-1234-5678","9.9","Low","192.168.2.1","qqq","000"

使用命令:sed 'N;s/","/","YYYY-MM-DD HH:MM:SS","/5' FILENAME 我在第5场后添加日期。通常这有效,但经常 CSV文件中的某些值会使此计数混乱,从而将日期插入第5个字段。要解决此问题,我怎样才能不仅在第5个字段后添加日期,还要确保第5个字段是IP地址?

最终输出应为:

"12345","","","None","192.168.2.1","YYYY-MM-DD HH:MM:SS","qqq","000"
"67890","ABC-1234-5678","9.9","Low","192.168.2.1","YYYY-MM-DD HH:MM:SS","qqq","000"

请回答使用SED而非AWK完成此操作的方法。以及如何在添加日期之前确保第5个字段也是IP地址?

2 个答案:

答案 0 :(得分:2)

此答案目前假设CSV文件非常一致且简单(如示例数据中所示),因此:

  • 字段周围总是有双引号。
  • 永远不会有像"…""…"这样的字段来表示字符串中嵌入的双引号。
  • 引号("this,that")之间永远不会有逗号括号。

鉴于这些先决条件,这个sed脚本完成了这项任务:

sed 's/^\("[^"]*",\)\{4\}"\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}",/&"YYYY-MM-DD HH:MM:SS",/'

让我们将搜索模式分成几部分:

  • ^\("[^"]*",\)\{4\}

    匹配行的开头,然后是:4次重复双引号,零序或多次非双引号,双引号和逗号。

    换句话说,这标识了前四个字段。

  • "\([0-9]\{1,3\}\.\)\{3\}

    匹配双引号,然后3个重复的1-3个十进制数字后跟一个点 - IPv4点分十进制地址的前三个三元组。

  • [0-9]\{1,3\}",

    匹配1-3个十进制数字后跟双引号和逗号 - IPv4点分十进制地址的最后一个三元组加上一个字段的结尾。

显然,对于您还需要处理的CSV文件的每个特性,您必须修改正则表达式。这不是微不足道的。

使用扩展的正则表达式(在GNU和BSD -E上由sed启用),您可以写:

sed -E 's/^("(([^"]*"")*[^"]*)",){4}"([0-9]{1,3}\.){3}[0-9]{1,3}",/&"YYYY-MM-DD HH:MM:SS",/'

识别前4个字段的模式比以前更复杂。它匹配4次重复:双引号,零次或多次出现{零次或多次非双引号后跟两次双引号}后跟零次或多次非双引号后跟双引号和逗号。

你也可以在经典sed(基本正则表达式)中用自由的反斜杠写出来:

sed 's/^\("\(\([^"]*""\)*[^"]*\)",\)\{4\}"\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}",/&"YYYY-MM-DD HH:MM:SS",/'

给定数据文件:

"12345","","","None","192.168.2.1","qqq","000"
"67890","ABC-1234-5678","9.9","Low","192.168.2.1","qqq","000"
"23456","Quaternions","2.3","Pisces","Heredotus","qqq","000"
"34567","Commas, oh commas!","3.14159","""Quotes"" quoth he","192.168.99.37","zzz","011"
"45678","Commas, oh commas!","3.14159","""Quote me"",""or not""","192.168.99.37","zzz","011"

显示的第一个脚本产生输出:

"12345","","","None","192.168.2.1","YYYY-MM-DD HH:MM:SS","qqq","000"
"67890","ABC-1234-5678","9.9","Low","192.168.2.1","YYYY-MM-DD HH:MM:SS","qqq","000"
"23456","Quaternions","2.3","Pisces","Heredotus","qqq","000"
"34567","Commas, oh commas!","3.14159","""Quotes"" quoth he","192.168.99.37","zzz","011"
"45678","Commas, oh commas!","3.14159","""Quote me"",""or not""","192.168.99.37","zzz","011"

前两行正确映射;第三个是正确不变的,但最后两个应该已经映射而不是。

第二个和第三个命令产生:

"12345","","","None","192.168.2.1","YYYY-MM-DD HH:MM:SS","qqq","000"
"67890","ABC-1234-5678","9.9","Low","192.168.2.1","YYYY-MM-DD HH:MM:SS","qqq","000"
"23456","Quaternions","2.3","Pisces","Heredotus","qqq","000"
"34567","Commas, oh commas!","3.14159","""Quotes"" quoth he","192.168.99.37","YYYY-MM-DD HH:MM:SS","zzz","011"
"45678","Commas, oh commas!","3.14159","""Quote me"",""or not""","192.168.99.37","YYYY-MM-DD HH:MM:SS","zzz","011"

请注意,未正确修改Heredotus,最后两行获取IP地址后添加的日期字符串(也正确)。

那些最后的正则表达式不适合胆小的人。

显然,如果你想坚持IP地址只匹配每个组件中0..255范围内的数字,没有前导0,那么你必须加强正则表达式的IP地址匹配部分。可以办到;它不漂亮。使用扩展正则表达式最简单:

([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])

您可以在之前显示的正则表达式中使用该单位代替每个[0-9]{3}单位。

请注意,这仍然不会尝试处理未被双引号括起来的字段。

它也不确定要从date命令替换的值。这是可行的(如果不是基本的)例程shell脚本仔细管理引号:

dt=$(date +'%Y-%m-%d %H:%M:%S')
sed -E 's/^("(([^"]*"")*[^"]*)",){4}"([0-9]{1,3}\.){3}[0-9]{1,3}",/&"'"$dt"'",/'

'…"'"$dt"'",/'序列是以单引号字符串开头的部分。第一个双引号是字符串中的简单数据;下一个单引号结束引用,"$dt"在shell双引号内插入date的值(因此空格不会造成任何麻烦),然后单引号恢复单引号符号,在字符串(sed的参数)之前添加另一个双引号,逗号和斜杠终止。

答案 1 :(得分:1)

尝试:

awk -vdate1=$(date +"%Y-%m-%d") -vdate2=$(date +"%H:%M:%S") -F, '$5 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]/{$5=$5 FS date1 " " date2} 1' OFS=,   Input_file

此外,如果您想编辑相同的Input_file,您可以将上面的命令输出到临时文件中,然后将命令重命名(mv命令)到同一个Input_file

现在也添加单线形式的解决方案。

awk -vdate1=$(date +"%Y-%m-%d") -vdate2=$(date +"%H:%M:%S") -F, '
            $5 ~ /[0-9]+\.[0-9]+\.[0-9]+\.[0-9]/{
            $5=$5 FS date1 " " date2
                                                }
            1
    '  OFS=,    Input_file