我想合并两个文件,例如如何根据两列的匹配来合并两个文件?但一个文件可能无法包含所有结果。例如
文件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 中(反之亦然),这将以某种方式显示它,以便您可以知道该唯一行来自哪个文件(与添加
-a2
到join
命令不同)。
这里有一些更全面的示例输入/输出用于测试:
$ 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
命令中,则 file1 中任何在 file2 中没有匹配键的行也将被打印。
# join -a1 file1 file2
1 dog woof
2 cat meow
3 fox
4 cow mooh
请注意,这不会处理来自 file2 的不可配对的行,因此如果在 file2 中您有另一行类似以下内容:
5 quack
该行不会被打印。您还可以-a2
在命令中添加标志join
,该标志将打印 file2 中的行,但这只会增加一些混乱,因为您不知道该行是来自 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