文件转换

文件转换

我有一个格式为两列的文件;-

A,val1
A,val2
A,val3
B,val1
B,val2
B,val3

我需要的是能够转换它的输出,以便第二列对于第一列中的每个唯一值都是水平的;-

A,val1,val2,val3
B,val1,val2,val3

我不确定最好的方法是使用 BASH 还是 AWK - 也​​许两者结合?如果有人可以请指出我正确的方向。

答案1

单独使用awk:

$ awk -F, 'BEGIN{OFS=FS} {a[$1] = a[$1] == "" ? $2 : a[$1] FS $2} END {for(i in a) print i,a[i]}' file
A,val1,val2,val3
B,val1,val2,val3

请注意,输出顺序无法保证 - 使用 GNU awk 很容易修复,但使用其他实现则更困难。输入数据不需要排序。

否则,使用 GNU datamash

datamash -t, groupby 1 collapse 2 < file

(如果输入未排序,请添加-s)或与 Miller

mlr --nidx --fs ',' nest --implode --values --across-records --nested-fs ',' -f 2 file

或更紧凑的更新版本

mlr --nidx --fs ',' nest --ivar ',' -f 2 file

答案2

有多种方法可以使用 shell 脚本解决这个问题,但我更喜欢使用一个还不太标准的工具:磨坊主。您可以apt install miller在 Ubuntu/Debian 上安装它。我发现 Miller 的动词是比 bash 或 awk 更自然的思考此类问题的工具。

如果问题中指定的数据存储在INPUT_FILE

A,val1
A,val2
A,val3
B,val1
B,val2
B,val3

然后是米勒的nest动词可用于将多个记录(行)打包为字段 2 中有多个值的单个记录,并将字段 2 扩展为多个字段:

mlr --ocsv --headerless-csv-output \
  nest --implode --values --across-records -f 2 then \
  nest --explode --values --across-fields -f 2 INPUT_FILE

这会产生您想要的输出:

A,val1,val2,val3
B,val1,val2,val3

Miller 可能有一种更简单的方法来做到这一点,但这是我找到的第一个解决方案。

答案3

为了保证输出的顺序,请使用以下 awk 代码。在这里,我们维护一个散列,也称为关联数组,seen[...],每当遇到新键 ($1) 时,它就会以递增的 kounter 为键。

$ awk -F "," '
    prev != $1 { prev = $1 }
    !($1 in a) { seen[++n] = $1 }
    { a[$1] = a[$1] FS $2 }
    END {
      for (i=1; i<=n; i++) {
        print seen[i] a[seen[i]] 
      }
    }
  ' file
A,val1,val2,val3
B,val1,val2,val3

答案4

在每个 Unix 机器上的任何 shell 中使用任何 awk 并保留输出行顺序,同时在内存中一次仅存储 1 个 $1 键控块:

$ awk '
    BEGIN { FS=OFS="," }
    $1!=p { printf "%s%s", rec, sep; rec=p=$1; sep=ORS }
    { rec = rec OFS $2 }
    END { print rec }
' file
A,val1,val2,val3
B,val1,val2,val3

相关内容