向具有 6 个字段(而不是 7 个)的记录的文件添加额外字段

向具有 6 个字段(而不是 7 个)的记录的文件添加额外字段

我对用逗号分隔的文本文件有问题,当我要开始接收具有混合布局的文件(数百个)时,某些记录将有 7 个字段(总共 6 个逗号),而同一文件中的其他记录将有 6 个字段(总共 5 个逗号)。

当我找到一条包含 5 个逗号的记录时,我想在该记录的末尾添加一个逗号,后跟 NA,这样我的加载过程就会认为它有 7 个字段,即最后一个 NA。

这就是我现在所拥有的,第一条记录有 7 个字段,第二条记录只有 6 个:

200000003183000100,Data,NA,0,IN,0,0.00
200000004625000000,Data,NA,0,IN,0

这就是我所期望的(两条记录都有 7 个字段)

200000003183000100,Data,NA,0,IN,0,0.00
200000004625000000,Data,NA,0,IN,0,NA

可以用 sed 或类似的东西计算逗号并在文件末尾添加 ,NA 来完成,每当只有 5 个逗号时。请记住,这将发生在数百个文件上,所以我不知道是否需要使用文件名作为参数,诸如此类。

答案1

如果被允许:

awk -F, 'NF==6{$0=$0",NA"}1' file

答案2

如同吉尔·奎诺的回答,但将新字段添加为新字段而不是当前行末尾的字符串。此外,允许在命令行上配置分隔符和预期字段数,并将使用字符串填充缺少的字段NA

$ awk -F , -v nf=7 'BEGIN { OFS = FS } { for (i = NF+1; i <= nf; ++i ) $i = "NA" }; 1' file
200000003183000100,Data,NA,0,IN,0,0.00
200000004625000000,Data,NA,0,IN,0,NA
$ awk -F , -v nf=12 'BEGIN { OFS = FS } { for (i = NF+1; i <= nf; ++i ) $i = "NA" }; 1' file
200000003183000100,Data,NA,0,IN,0,0.00,NA,NA,NA,NA,NA
200000004625000000,Data,NA,0,IN,0,NA,NA,NA,NA,NA,NA

这显然假设输入是在简单的 CSV格式(不包含嵌入逗号或换行符的字段)。

答案3

为了提高 awk 的效率,除非必要,否则不要修改记录 ( $0) 或字段 ( $1、等)。$2这不会修改它们中的任何一个:

awk -F, '{print $0 (NF==6 ? ",NA" : "")}'

其他现有的 awk 答案要么修改 $0 要么修改字段,这两者都会减慢处理速度。

这些直接更改 $0 (因此间接添加一个字段):

awk -F, 'NF==6{$0=$0",NA"}1'
awk '/(.*,){6}/ || sub(/$/,",NA")'
awk '!/(.*,){6}/{$0=$0",NA"}1'

这需要 awk 找到新的内存来$0移动,因为它的大小增加了(连接或以其他方式更改变量的大小是 awk 中最慢的操作之一),并导致 awk 重新分割$0为字段。

这些直接更改字段(因此间接更改 $0):

awk -F, -v nf=7 'BEGIN { OFS = FS } (NF < nf){ $(nf)="N/A"}1'
awk -F, -v nf=7 'BEGIN { OFS = FS } { for (i = NF+1; i <= nf; ++i ) $i = "NA" }; 1'

这会导致 awk 从其字段重建,并且由于大小增加,$0再次需要 awk 寻找新的内存来移入$0

要使用 GNU awk 在数百个(但少于 ARG_MAX)个 CSV 文件上运行任何 awk 脚本,只需:

awk -i inplace 'script' file*.csv

或使用任何 awk:

tmp=$(mktemp)
for file in file*.csv; do
    awk 'script' "$file" > "$tmp" &&
    mv -- "$tmp" "$file"
done

答案4

Posix sed,我们尝试更改第 6 个逗号,如果成功,我们将打印并返回读取下一条记录(测试命令t) 否则,将,NA字符串附加到当前记录的末尾。

sed '
  s/,/,/6;t
  s/$/,NA/
' file

使用awk,一种方法是:

awk '/(.*,){6}/ || sub(/$/,",NA")' file

当您需要对多个文件执行此操作时,用例是通过命令find

假设您的 CSV 文件命名为*.csv

find . -type f -name '*.csv' -exec \
  sed -i 's/,/,/6;t' -e 's/$/,NA' {} +

对于 awk 的情况,如果您的 awk 支持,请使用 inplace 选项(GNU awk 版本 4.1.0 及以上)

find . -type f -name "*.csv" -exec \
awk -i inplace '!/(.*,){6}/{$0=$0",NA"}1' {} +

符号{} +将多个文件名汇集到 awk 或 sed 命令行,以便最大限度地减少对这些实用程序的调用

笔记:

  • 该文件有 5 个或 6 个逗号。
  • 字段本身不能包含逗号。
  • 行尾为 Linux 风格 (\n)。

相关内容