我有一个处理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
答案 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
实现支持。