私のファイルのデータは次のとおりです。
field11|field12|field13
field11|field12|field23
field11|field32|field33
field41|field42|field43
field41|field52|field43
field41|field62|field63
ご覧のとおり、私は2つのIDを持っており、field11
次field41
のようにそのIDが一度だけ表示されるようにしたいと思います。
field11|{'field12','field32'}|field13
field41|{'field42','field52','field62'}|field43
私は3番目のフィールドがそのIDの最初の項目と同じであることを望みます。各id($ 1)は$ 3が異なりますが、最初または最初と2番目のフィールドは同じままです。ただし、そのIDの最初の行の内容を印刷する必要があります。例に示すように、field23の代わりにfield13を印刷します。
私はawk
これを/として達成しようとしていますsed
。私はシェルの基本ループを使ってこれを達成するソリューションを知っています。ただし、awk
これは他の同様のツールで行う必要があります。
答え1
やや長い(しかし理解しやすい)awk
解決策:
BEGIN { FS = OFS = "|" }
function output() {
if (FNR == 1) return
data = ""
for (i in col2) {
qi = sprintf("'%s'", i);
data = (data == "" ? qi : data "," qi)
}
print col1, sprintf("{%s}", data), col3
}
$1 == col1 && !($2 in col2) { col2[$2] }
$1 != col1 {
output()
col1 = $1; col3 = $3
delete col2; col2[$2]
}
END { output() }
このBEGIN
ブロックは、単に入力と出力フィールドの区切り記号を|
。
この関数は、(最初の列のID)、(2番目の列の一意のデータ配列)、および(3番目の列の対応する特定のIDの最初のデータ項目)から収集されたデータを取得してoutput()
出力します。のキーを繰り返して個別に引用し、間にカンマを使用して文字列変数に追加します。次に、(中括弧内)とを印刷します。col1
col2
col3
col2
data
col1
data
col3
その特定のIDに対して以前に一度も見たことがなかった2番目の列のエントリが見つかった場合は、次のブロックが実行されます。 2番目の列をのキーとして追加するだけですcol2
。
最初の列で新しいIDが見つかると、次のブロックが実行されます。収集した変数を呼び出してoutput()
リセットして、新しいIDのデータ収集を開始します。
END
ブロックはoutput()
最後のIDの出力データを呼び出します。
プログラムはファイル全体を一度にメモリに保存しようとはしませんが、データを最初の列でソートする必要があります。
提供されたデータに対して実行します。
$ awk -f script.awk file
field11|{'field12','field32'}|field13
field41|{'field42','field62','field52'}|field43
答え2
Perlのいくつかの粗い部分:
perl -F'\|' -lane '
$f2{ $F[0] }{ $F[1] } = 1;
$f3{ $F[0] } = $F[2] if not exists $f3{ $F[0] };
} END {
for $key (sort keys %f2) {
printf "%s|{%s}|%s\n",
$key,
join(",", map {chr(39) . $_ . chr(39)} sort keys %{$f2{$key}}),
$f3{$key};
}
' file
最初の2行は連想配列を使用してデータを蓄積します。
その後、END ブロックはデータ、書式設定、印刷を繰り返します。