両方のファイルをマージし、両方のファイルの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の項目のみを評価します。
FNR
NR
共通性ベースの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