Shell脚本CSV处理 - 使用AWK添加新列

时间:2016-04-20 10:16:04

标签: bash shell csv awk

我有一个处理CSV文件的shell脚本。特别是一步是添加一个列并在其中放置默认值“null”。我得到了预期的更改,只是要添加的新列被添加到下一行而不是相同的行。

任何人都可以在代码中提出错误并导致这种意外更改吗?

CODE:

awk 'BEGIN{FS=",";OFS=";"} {$(NF+1) = NR==1 ? "NewColm" : "NULL"} 1' source.csv > final.csv

输入CSV:

OldColm1,OldColm2,OldColm3,OldColm4,OldColm5,OldColm6
Value1,Value2,Value3,Value4,Value5,Value6

输出CSV:

OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6
;NewColm
Value1;Value2;Value3;Value4;Value5;Value6
;NULL

预期CSV:

OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm
Value1;Value2;Value3;Value4;Value5;Value6;NULL

1 个答案:

答案 0 :(得分:1)

正如评论中所解释的那样,这是由于行被\r\n而不是\n分隔而造成的。

od程序可用于说明这一点:

cat source_dos.csv
OldColm1,OldColm2,OldColm3,OldColm4,OldColm5,OldColm6
Value1,Value2,Value3,Value4,Value5,Value6
od -c source_dos.csv
0000000   O   l   d   C   o   l   m   1   ,   O   l   d   C   o   l   m
0000020   2   ,   O   l   d   C   o   l   m   3   ,   O   l   d   C   o
0000040   l   m   4   ,   O   l   d   C   o   l   m   5   ,   O   l   d
0000060   C   o   l   m   6  \r  \n   V   a   l   u   e   1   ,   V   a
0000100   l   u   e   2   ,   V   a   l   u   e   3   ,   V   a   l   u
0000120   e   4   ,   V   a   l   u   e   5   ,   V   a   l   u   e   6
0000140  \r  \n
0000142
awk 'BEGIN{FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv
;NewColm;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6
;NULL1;Value2;Value3;Value4;Value5;Value6
awk 'BEGIN{FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv | od -c
0000000   O   l   d   C   o   l   m   1   ;   O   l   d   C   o   l   m
0000020   2   ;   O   l   d   C   o   l   m   3   ;   O   l   d   C   o
0000040   l   m   4   ;   O   l   d   C   o   l   m   5   ;   O   l   d
0000060   C   o   l   m   6  \r   ;   N   e   w   C   o   l   m  \n   V
0000100   a   l   u   e   1   ;   V   a   l   u   e   2   ;   V   a   l
0000120   u   e   3   ;   V   a   l   u   e   4   ;   V   a   l   u   e
0000140   5   ;   V   a   l   u   e   6  \r   ;   N   U   L   L  \n
0000157

评论中提供的解决方法解决方案是将输入从DOS转换为\r}到UNIX - 类似({{ 1}})输入:

\n
cp source_dos.csv source_unix.csv && dos2unix source_unix.csv
dos2unix: converting file source_unix.csv to Unix format ...
od -c source_unix.csv
0000000   O   l   d   C   o   l   m   1   ,   O   l   d   C   o   l   m
0000020   2   ,   O   l   d   C   o   l   m   3   ,   O   l   d   C   o
0000040   l   m   4   ,   O   l   d   C   o   l   m   5   ,   O   l   d
0000060   C   o   l   m   6  \n   V   a   l   u   e   1   ,   V   a   l
0000100   u   e   2   ,   V   a   l   u   e   3   ,   V   a   l   u   e
0000120   4   ,   V   a   l   u   e   5   ,   V   a   l   u   e   6  \n
0000140
awk 'BEGIN{FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv
OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm
Value1;Value2;Value3;Value4;Value5;Value6;NULL
awk 'BEGIN{FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv | od -c

0000000 O l d C o l m 1 ; O l d C o l m 0000020 2 ; O l d C o l m 3 ; O l d C o 0000040 l m 4 ; O l d C o l m 5 ; O l d 0000060 C o l m 6 ; N e w C o l m \n V a 0000100 l u e 1 ; V a l u e 2 ; V a l u 0000120 e 3 ; V a l u e 4 ; V a l u e 5 0000140 ; V a l u e 6 ; N U L L \n 0000155 - 解决此问题的唯一方法是相应地调整记录分隔符 awk

RS以及输出记录分隔符 RS的对应部分,默认为ORS。 这就是为什么在\n输入的情况下,\r\n仍然是最后一个输入列的一部分,而您的新列会被卡住'在此\r\r之间添加\n

更改ORS解决了这个问题:

RS
awk 'BEGIN{RS="\r\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv

请注意,这仍会创建OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm Value1;Value2;Value3;Value4;Value5;Value6;NULL - 类似(UNIX)输出:

\n
awk 'BEGIN{RS="\r\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv | od -c

要生成0000000 O l d C o l m 1 ; O l d C o l m 0000020 2 ; O l d C o l m 3 ; O l d C o 0000040 l m 4 ; O l d C o l m 5 ; O l d 0000060 C o l m 6 ; N e w C o l m \n V a 0000100 l u e 1 ; V a l u e 2 ; V a l u 0000120 e 3 ; V a l u e 4 ; V a l u e 5 0000140 ; V a l u e 6 ; N U L L \n 0000155 - 类似(DOS)输出,只需调整\r\n

ORS
awk 'BEGIN{RS="\r\n";ORS=RS;FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv
OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm
Value1;Value2;Value3;Value4;Value5;Value6;NULL
awk 'BEGIN{RS="\r\n";ORS=RS;FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv | od -c

但请注意,对于0000000 O l d C o l m 1 ; O l d C o l m 0000020 2 ; O l d C o l m 3 ; O l d C o 0000040 l m 4 ; O l d C o l m 5 ; O l d 0000060 C o l m 6 ; N e w C o l m \r \n V 0000100 a l u e 1 ; V a l u e 2 ; V a l 0000120 u e 3 ; V a l u e 4 ; V a l u e 0000140 5 ; V a l u e 6 ; N U L L \r \n 0000157 - 类似(UNIX)输入,这将失败:

\n
awk 'BEGIN{RS="\r\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv
OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6
Value1;Value2;Value3;Value4;Value5;Value6
;NewColm
awk 'BEGIN{RS="\r\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv | od -c

为什么我认为这比使用0000000 O l d C o l m 1 ; O l d C o l m 0000020 2 ; O l d C o l m 3 ; O l d C o 0000040 l m 4 ; O l d C o l m 5 ; O l d 0000060 C o l m 6 \n V a l u e 1 ; V a l 0000100 u e 2 ; V a l u e 3 ; V a l u e 0000120 4 ; V a l u e 5 ; V a l u e 6 \n 0000140 ; N e w C o l m \n 0000151 更好: 使用正则表达式(RE)作为dos2unix可以使其适用于RS \n - 分隔输入无需知道这两者中的哪一个

\r\n
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv
OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm
Value1;Value2;Value3;Value4;Value5;Value6;NULL
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv

在这两种情况下,都会生成OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm Value1;Value2;Value3;Value4;Value5;Value6;NULL - like(UNIX)输出:

\n
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv | od -c
0000000   O   l   d   C   o   l   m   1   ;   O   l   d   C   o   l   m
0000020   2   ;   O   l   d   C   o   l   m   3   ;   O   l   d   C   o
0000040   l   m   4   ;   O   l   d   C   o   l   m   5   ;   O   l   d
0000060   C   o   l   m   6   ;   N   e   w   C   o   l   m  \n   V   a
0000100   l   u   e   1   ;   V   a   l   u   e   2   ;   V   a   l   u
0000120   e   3   ;   V   a   l   u   e   4   ;   V   a   l   u   e   5
0000140   ;   V   a   l   u   e   6   ;   N   U   L   L  \n
0000155
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv | od -c

要根据输入类型设置输出类型,可以将每个记录0000000 O l d C o l m 1 ; O l d C o l m 0000020 2 ; O l d C o l m 3 ; O l d C o 0000040 l m 4 ; O l d C o l m 5 ; O l d 0000060 C o l m 6 ; N e w C o l m \n V a 0000100 l u e 1 ; V a l u e 2 ; V a l u 0000120 e 3 ; V a l u e 4 ; V a l u e 5 0000140 ; V a l u e 6 ; N U L L \n 0000155 设置为与ORS RE,RS匹配的实际文本:

RT
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {ORS=RT}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv
OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm
Value1;Value2;Value3;Value4;Value5;Value6;NULL
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {ORS=RT}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv
OldColm1;OldColm2;OldColm3;OldColm4;OldColm5;OldColm6;NewColm
Value1;Value2;Value3;Value4;Value5;Value6;NULL
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {ORS=RT}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_dos.csv | od -c
0000000   O   l   d   C   o   l   m   1   ;   O   l   d   C   o   l   m
0000020   2   ;   O   l   d   C   o   l   m   3   ;   O   l   d   C   o
0000040   l   m   4   ;   O   l   d   C   o   l   m   5   ;   O   l   d
0000060   C   o   l   m   6   ;   N   e   w   C   o   l   m  \r  \n   V
0000100   a   l   u   e   1   ;   V   a   l   u   e   2   ;   V   a   l
0000120   u   e   3   ;   V   a   l   u   e   4   ;   V   a   l   u   e
0000140   5   ;   V   a   l   u   e   6   ;   N   U   L   L  \r  \n
0000157
awk 'BEGIN{RS="\r?\n";FS=",";OFS=";"}
     {ORS=RT}
     {$(NF+1) = NR==1 ? "NewColm" : "NULL"}
     1
    ' source_unix.csv | od -c

请注意,使用0000000 O l d C o l m 1 ; O l d C o l m 0000020 2 ; O l d C o l m 3 ; O l d C o 0000040 l m 4 ; O l d C o l m 5 ; O l d 0000060 C o l m 6 ; N e w C o l m \n V a 0000100 l u e 1 ; V a l u e 2 ; V a l u 0000120 e 3 ; V a l u e 4 ; V a l u e 5 0000140 ; V a l u e 6 ; N U L L \n 0000155 作为RE以及RS 内置变量是GNU RT({{1} })扩展,并且可能不受所有awk实现支持。

相关问题