潜在的に不完全なキーに基づくファイルのマージ

潜在的に不完全なキーに基づくファイルのマージ

次の2つのファイルをマージしたい2つの列の一致に基づいて2つのファイルをマージする方法は?ただし、1つのファイルにすべての結果が含まれているとは限りません。例えば

ファイル1

1 dog
2 cat
3 fox
4 cow

ファイル2

1 woof
2 meow
4 mooh

希望の出力

1 dog woof
2 cat meow
3 fox
4 cow mooh

答え1

配列の配列を処理するには、GNU awkを使用します。

$ awk '{a[$1][(NR>FNR)]=$2} END{for (i in a) print i, a[i][0], a[i][1]}' file{1,2}
1 dog woof
2 cat meow
3 fox
4 cow mooh

または awk を使用してください。

$ awk '{keys[$1]; a[$1,(NR>FNR)]=$2} END{for (i in keys) print i, a[i,0], a[i,1]}' file{1,2}
1 dog woof
2 cat meow
3 fox
4 cow mooh

上記の出力は最初のフィールドの数値の昇順ですが、これは幸運/偶然です。出力行の順序は、実際には「in」演算子によって提供される「ランダム」(通常はハッシュ順序)です。興味がある場合は、出力をパイプで接続しますsort -k1,1n(またはPROCINFO["sorted_in"]="@ind_num_asc"GNU awkのENDセクションの先頭に設定します)。

このソリューションとソリューションの重要な違いは次のとおりjoinです。

  1. これは、入力がソートされず、joinキーフィールドで入力をソートする必要がある場合にも機能します。
  2. file2にfile1にないキーを持つ行がある場合(またはその逆の場合)、add -a2tojoinコマンドとは異なり、その一意の行がどのファイルから来たのかがわかるように表示します。

以下は、テストのためのより包括的な入力/出力の例です。

$ head file{1,2}
==> file1 <==
1 dog
2 cat
4 cow
5 bear

==> file2 <==
1 woof
2 meow
3 growl
4 mooh

その後、上記のawkスクリプトを実行して同じ出力を取得できます。

$ awk '{a[$1][(NR>FNR)]=$2} END{for (i in a) print i, a[i][0], a[i][1]}' file{1,2}
1 dog woof
2 cat meow
3  growl
4 cow mooh
5 bear

そして3 growl、前に余分なスペースがあるので、growlこれがfile2の唯一の縮小であることがわかりますjoin。代わりに、以下を使用してください。

$ join -a1 -a2 file1 file2
1 dog woof
2 cat meow
3 growl
4 cow mooh
5 bear

file1の一意の行(例:)5 bearとfile2の一意の行(例3 growl)を区別することはできません。

答え2

file1とfile2の両方がソートされていると仮定すると、デフォルトjoinでは両方のファイルにキーがある行のみが関連付けられます。したがって、あなたの場合、file2にキーが「3」の行がないため、その行は結合されません。ただし、この動作を変更できます。

次のマニュアルページからjoin

   -a FILENUM
          also print unpairable lines from file FILENUM, where FILENUM is 1 or 2, corresponding to FILE1 or FILE2

したがって、-a1このフラグをjoinコマンドに追加すると、file2に一致するキーを持たないfile1のすべての行も印刷されます。

# join -a1 file1 file2
1 dog woof
2 cat meow
3 fox
4 cow mooh

これはfile2でペアリングできない行を処理しないため、file2には次のような他の行があります。

5 quack

この行は印刷されません。 file2 の行を印刷する-a2コマンドにフラグを追加することもできますが、join行が file1 から来たのか、それとも file2 から来たのかわからないため、混乱だけが重み付けされます。

答え3

キーと値を連想配列に入れてfile3に印刷します。

declare -A arr

while read key value
do
    if [ -z ${arr[$key]} ]; then
        arr[$key]=$value
    else
        arr[$key]="${arr[$key]} $value"
    fi
done < <(cat file1 file2)

echo -n > file3

for key in "${!arr[@]}"
do
    echo "$key ${arr[$key]}" >> file3
done

関連情報