2つの列で構成されるファイルがあります。
A,val1
A,val2
A,val3
B,val1
B,val2
B,val3
私にとって必要なのは、最初の列のすべての固有値に対して、2番目の列が水平になるように出力を変換できることです。
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
シェルスクリプトを使用してこの問題を解決する方法はいくつかありますが、私はあまり標準的ではないツールを使用することを好みます。ミラー。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 コードを使用します。ここでは、連想配列とも呼ばれるハッシュ(see [...])を維持します。このハッシュは、新しいキー($ 1)が見つかるたびに増加するカウンターによって入力されます。
$ 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システムのすべてのシェルで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