awkを介して複数のファイルをN個の共通列にマージします。ファイルに共通キーがない場合は、列の値をゼロに変更したいと思います。

awkを介して複数のファイルをN個の共通列にマージします。ファイルに共通キーがない場合は、列の値をゼロに変更したいと思います。

共通列に基づいて複数のファイルをマージし、ファイルに対応する共通列がない場合はゼロを追加したいと思います。たとえば、下の図を参照してください。

a1.txt

111,222,444,5.5
121,321,555,1.2

a2.txt

111,222,444,7.8
333,321,555,4.5
311,555,222,1.1

a3.txt

333,321,555,9.1
311,555,222,8.8
444,666,777,2.5

一致は最初の3列の組み合わせでなければなりません。

出力は次のようになります。

111,222,444,5.5,7.8,0
121,321,555,1.2,0,0
333,321,555,0,4.5,9.1
311,555,222,0,1.1,8,8
444,666,777,0,0,2.5

3つの入力ファイルのうち4列目の値が異なるので、順番に配置したいと思います。 a1.txt値と同様に、出力ファイルの4列目にする必要があります。 a2.txtの値は出力ファイルの列5にあり、a3.txtの値は出力ファイルの列6になければなりません。以下で試しましたが、期待した結果が得られませんでした。

awk '{ a[$1 FS $2 FS $3 FS] = a[$1 FS $2 FS $3 FS] ( a[$1 FS $2 FS $3 FS] == "" ? "" : FS) $4 } END{ for (i in a){print i,a[i,0],a[i]} }' FS="," a1.txt a2.txt a3.txt

だから4つ、5つ、6つの入力ファイルに対して同じことをしたいと思います。誰でもこの問題を解決するのに役立ちますか?

ベストアンサー1

awkを使用し、出力でレコードの順序を維持します。

awk 'BEGIN{ SUBSEP=OFS=FS="," }
 FNR==1 && !reProccss{ fileNr++ }
 !reProccss{ keys[$1, $2, $3, fileNr]=$4; next }
  reProccss{ key=($1 OFS $2 OFS $3); recNr++
             for(i=1; i<=fileNr; i++)
                 if(seen[key]++<fileNr){
                     join[key]= join[key] OFS ((key, i) in keys ?keys[key, i]:"0")
                     data[recNr]= key join[key]
                 }
           }
END{ for(rec=1; rec<=recNr; rec++)
         if(data[rec]!="")
             print data[rec]
}' a[1-3].txt reProccss=1 a[1-3].txt

または、join+シェルを使用して複数の列をキーで単一のキーに変換し、次のようjoinな答えでコマンドを使用します。最初の列に複数のファイルをマージする(結合は単一の列をキーとして使用しなければ機能しないため)、目的の出力を生成します。

-そのため、最初の2つのファイルの特定の文字(入力ファイルに含まれてはいけない文字など)で区切って、複数のキー列を1つのキー列に変換して一時ファイルに出力します。joined.tmp:

join -t, -a1 -a2 -e 0 -o auto \
    <(<a1.txt sort |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') \
    <(<a2.txt sort |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') > joined.tmp

次に、シェルループを使用して残りのファイルを処理します。joined.tmpファイル(次のファイルに関連付けるために実行されるたびに更新されます)、ループですでに処理されている最初の2つのファイルもスキップされます。

for file in ./a*.txt; do
    [ "$file" = "./a1.txt" -o "$file" = "./a2.txt" ] && continue
    join -t, -a1 -a2 -e 0 -o auto \
        joined.tmp <(sort "$file" |awk -F, -v OFS='-' '{ print $1, $2, $3 FS $4 }') >joined.tmp.1
    mv joined.tmp.1 joined.tmp
done

最後に追加された文字を-元の文字に戻します,

sed 's/-/,/g' joined.tmp > joined-final.csv

join入力ファイルをソートする必要があるため、出力のレコードの順序が変わります。

$ cat joined-final.csv
111,222,444,5.5,7.8,0
121,321,555,1.2,0,0
311,555,222,0,1.1,8.8
333,321,555,0,4.5,9.1
444,666,777,0,0,2.5 

おすすめ記事