次の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
です。
- これは、入力がソートされず、
join
キーフィールドで入力をソートする必要がある場合にも機能します。 - file2にfile1にないキーを持つ行がある場合(またはその逆の場合)、add
-a2
tojoin
コマンドとは異なり、その一意の行がどのファイルから来たのかがわかるように表示します。
以下は、テストのためのより包括的な入力/出力の例です。
$ 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