【问题标题】:Shell script CSV processing - adding new column with AWKShell 脚本 CSV 处理 - 使用 AWK 添加新列
【发布时间】:2016-04-20 10:16:03
【问题描述】:

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

任何人都可以提出代码中的问题并导致这种意外更改吗?

代码:

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

【问题讨论】:

  • 你有 CRLF 行尾吗?还有value7 来自哪里?
  • value7 写错了,我现在更正了。我不知道如何识别 CRLF 行尾或 CRLF
  • 使用cat -v file。在行尾查找^M
  • 是的,每行末尾都有“^M”
  • 先把文件转成UNIX格式dos2unix source.csv

标签: bash shell csv awk


【解决方案1】:

正如 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 实现。

【讨论】:

    猜你喜欢
    • 2023-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-19
    • 1970-01-01
    相关资源
    最近更新 更多