AWKを使用して2つのファイルをマージし、2番目の列を追加します。

AWKを使用して2つのファイルをマージし、2番目の列を追加します。

両方のファイルをマージし、両方のファイルの2番目の列を追加したいと思います。

ファイル1.

001    A
002    B
003    C
004    D

ファイル2.

002    D
003    D
005    E
006    F

以下のようにファイル3にマージする必要があります。

001    A
002    BD
003    CD
004    D
005    E
006    F

次のコマンドを実行しましたが、出力が正しくありません。

$ awk 'FNR==NR{a[$1]=$2; next}{print $1, a[$1]$2}' file1 file2
002    BD
003    CD
005    E
006    F

awkまたはsedを使用して結合する方法を教えてください。

答え1

左/右外部結合と完全外部結合

使用中のコマンドの問題は、file2のすべての項目がfile1になければならないことです。ステートメントの印刷部分は、file2の項目のみを評価します。

FNRNR共通性ベースのJOINにこのように頻繁に使用されます。つまり、あるデータセットのすべてのレコードを選択し、別のデータセットの関連レコードのみを選択します。具体的に実装したのは、「右」 - file2のすべての項目と左のfile1から一致するメンバーを取得する「RIGHT OUTER JOIN」です。

代わりに「FULL OUTER JOIN」を実行しようとしています。両方のファイルのすべてのレコードは、列1のマージレコードに基づいています。

FNR==NR「FILE」レコード数(FNR)がレコード全体の数(NR)に等しいことを示します。NR処理されたファイルの各行は増加し、FNR新しいファイルを起動するとゼロにリセットされます。FNR==NRこれは、最初のファイルを読み取ると同時に2つ以上のファイルをインポートする場合にのみ当てはまります。 awkが次のファイルに切り替わると、FNRはゼロにリセットされ、NRは増加し続けます。

これを説明するために、awkが入力を処理するときにこれらの変数の状態を提供するprintステートメントを挿入しました。

$> awk 'FNR==NR{a[$1]=$2;printf("File: %s, NR: %s, FNR: %s, $1: %s, $2: %s, a[$1]: %s\n",FILENAME,NR,FNR,$1,$2,a[$1]); next}  {printf("File: %s, NR: %s, FNR: %s, $1: %s, $2: %s, a[$1]$2: %s\n",FILENAME,NR,FNR,$1,$2,a[$1]$2); }' file1 file2

File: file1, NR: 1, FNR: 1, $1: 001, $2: A, a[$1]: A
File: file1, NR: 2, FNR: 2, $1: 002, $2: B, a[$1]: B
File: file1, NR: 3, FNR: 3, $1: 003, $2: C, a[$1]: C
File: file1, NR: 4, FNR: 4, $1: 004, $2: D, a[$1]: D
File: file2, NR: 5, FNR: 1, $1: 002, $2: D, a[$1]$2: BD
File: file2, NR: 6, FNR: 2, $1: 003, $2: D, a[$1]$2: CD
File: file2, NR: 7, FNR: 3, $1: 005, $2: E, a[$1]$2: E
File: file2, NR: 8, FNR: 4, $1: 006, $2: F, a[$1]$2: F

解決策

この問題を解決するには、file2の処理中に配列にエントリを追加し続け、すべての入力ファイルが処理された後に結果を出力するだけです。

したがって、この場合、私たちは実際にはまったく興味を持っていNRません。FNR

  • すべての入力ファイルの各テキスト行に対して、最初の列の値を配列$1のインデックスとして使用します。a[$1]

  • 列2の値を$2そのインデックスの配列に割り当てますが、すでに存在できる値を上書きしないように値を追加します。a[$1]=a[$1]$2

  • 配列を印刷する前に、すべてのレコード/行が処理されるのを待ちます。

    for (i in a) { printf("%s\t%s\n", i, a[i]) }

唯一の欠点は、awkが整数の代わりに文字列ベースの索引付けを使用する連想配列を使用することです(それで機能します)。ただし、副作用は、この場合、配列内の項目の順序が予想と異なる可能性があることです。印刷された内容は数値順(インデックスベース)ではないため、並べ替えるために出力をパイプで接続する必要があります。

$> awk '{ a[$1]=a[$1]$2; next } END { for (i in a) { printf("%s\t%s\n", i, a[i]) } }' file1 file2 | sort -n 

001     A
002     BD
003     CD
004     D
005     E
006     F

代替方法

Joinコマンドを使用してこれを行うこともできますが、フィールドを結合する方法がわかりません。それでもスペースで区切られているので、追加の処理ステップが必要です。

$> join  -o 0,1.2,2.2 -a1 -a2 file1 file2 | awk '{printf("%s\t%s%s\n", $1, $2, $3)}'
001     A
002     BD
003     CD
004     D
005     E
006     F

太陽

これは重複項目を拒否することには影響しません。これは望ましいかもしれないし、望ましくないかもしれない。現在、別の入力ファイルに重複レコードがある場合はマージされます。

file1: 001 A

file2: 001 A
出力が記録されます。
001 AA

答え2

今、あなたの研究結果を確認したので、次の解決策を提示します。

awk '{ z[$1]=z[$1]$2 } END { for (i in z) print i, z[i] }' file1  file2

出力は次のとおりです

002 BD
003 CD
004 D
005 E
006 F 
001 A

最初の列の値を数値でソートするには、前のコマンドの出力を次に渡しますsort

awk '{ z[$1]=z[$1]$2 } END { for (i in z) print i, z[i] }' file1  file2 \
| sort -n -k1

関連情報