正如 cmets 中所解释的,这是由 \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
cmets 中提供的workaround 解决方案是将输入从DOS-like (\r) 转换为UNIX-like (\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 的 \n 之间。
更改 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-like (\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-like (\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
为什么我认为这比使用dos2unix 更好:
使用 正则表达式 (RE) 作为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
在这两种情况下,都会生成UNIX-like (\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设置为与RS RE、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 以及 RT 内置变量 是 GNU awk (gawk) 扩展,可能并非所有人都支持awk 实现。