Bash 基于匹配列合并多个文件
我有很多文件(我举了5个为例) 如果与第一个文件不匹配,则应在输出中追加0 file1Bash 基于匹配列合并多个文件,bash,awk,Bash,Awk,我有很多文件(我举了5个为例) 如果与第一个文件不匹配,则应在输出中追加0 file1 1001 1 2 1002 1 2 1003 3 5 1004 6 7 1005 8 9 1009 2 3 文件2 1002 7 1003 8 file3 1001 5 1002 3 file4 1002 10 1004 60 1007 4 file5 1001 102 1003 305 1005 809 所需输出 1001 1 2 0 5 0 102 1002 1 2 7 3 10 0 1
1001 1 2
1002 1 2
1003 3 5
1004 6 7
1005 8 9
1009 2 3
文件2
1002 7
1003 8
file3
1001 5
1002 3
file4
1002 10
1004 60
1007 4
file5
1001 102
1003 305
1005 809
所需输出
1001 1 2 0 5 0 102
1002 1 2 7 3 10 0
1003 3 5 8 0 0 305
1004 6 7 0 0 60 0
1005 8 9 0 0 0 809
1007 0 0 0 0 4 0
1009 2 3 0 0 0 0
使用下面的代码,我可以合并两个文件,但如何合并所有文件
awk 'FNR==NR{a[$1]=$2;next}{print $0,a[$1]?a[$1]:"0"}' file2 file1
1001 1 2 0
1002 1 2 7
1003 3 5 8
1004 6 7 0
1005 8 9 0
提前感谢GNU加入救援
$ join -a1 -a2 -e '0' -o auto file1 file2 \
| join -a1 -a2 -e '0' -o auto - file3 \
| join -a1 -a2 -e '0' -o auto - file4 \
| join -a1 -a2 -e '0' -o auto - file5
选项-a1
和-a2
告诉join
插入缺少的字段。而-e'0'
告诉它用零替换它们。输出由-o auto
指定,它假定接受所有字段
当有大量文件时,不能使用管道构造,但可以使用简单的for循环:
out=output
tmp=$(mktemp)
[[ -e "$out" ]] && rm -rf "$out" || touch "$out"
for file in f*; do
join -a1 -a2 -e0 -o auto "$out" "$file" > "$tmp"
mv "$tmp" "$out"
done
cat "$out"
或者如果您真的喜欢管道:
pipeline="cat /dev/null"
for file in f*; do pipeline="$pipeline | join -a1 -a2 -e0 -o auto - $file"; done
eval "$pipeline"
这里非常有趣:
备注:在这种情况下,使用
auto
非常有用,但这不是问题的一部分。它是一个GNU扩展,是的一部分。纯POSIX版本会读起来有点麻烦,如下所示:
$ join -a1 -a2 -e '0' -o 0 1.2 2.2 file1 file2 \
| join -a1 -a2 -e '0' -o 0 1.2 1.3 2.2 - file3 \
| join -a1 -a2 -e '0' -o 0 1.2 1.3 1.4 2.2 - file4 \
| join -a1 -a2 -e '0' -o 0 1.2 1.3 1.4 1.5 2.2 - file5
有关使用GNU awk的
man join
的更多信息
awk '
NR>FNR && FNR==1{
colcount+=cols
}
{
for(i=2;i<=NF;i++){
rec[$1][colcount+i-1]=$i
}
}
{
cols=NF-1
}
END{
colcount++
for(ind in rec){
printf "%s%s",ind,OFS
for(i=1;i<=colcount;i++){
printf "%s%s",rec[ind][i]?rec[ind][i]:0,OFS
}
print ""
}
}' file{1..5} | sort -k1 | column -t
注意:适用于上述情况和任何类型的值 使用GNU awk实现真正的多维数组并按以下方式排序:
$ cat tst.awk
FNR==1 { numCols = colNr }
{
key = $1
for (i=2; i<=NF; i++) {
colNr = numCols + i - 1
val = $i
lgth = length(val)
vals[key][colNr] = val
wids[colNr] = (lgth > wids[colNr] ? lgth : wids[colNr])
}
}
END {
numCols = colNr
PROCINFO["sorted_in"] = "@ind_num_asc"
for (key in vals) {
printf "%s", key
for (colNr=1; colNr<=numCols; colNr++) {
printf "%s%*d", OFS, wids[colNr], vals[key][colNr]
}
print ""
}
}
$ awk -f tst.awk file*
1001 1 2 0 5 0 102
1002 1 2 7 3 10 0
1003 3 5 8 0 0 305
1004 6 7 0 0 60 0
1005 8 9 0 0 0 809
1007 0 0 0 0 4 0
1009 2 3 0 0 0 0
$cat tst.awk
FNR==1{numCols=colNr}
{
钥匙=1美元
对于(i=2;i wids[colNr]?lgth:wids[colNr])
}
}
结束{
numCols=colNr
PROCINFO[“排序在”]=“@ind\u num\u asc”
用于(输入VAL){
printf“%s”,键
对于(colNr=1;colNrAnd如果file5
有100666
,那么输出也会有1000666
?嗨,詹姆斯,如果可能的话,请回答是的..是的,我想澄清一下auto
是一个GNU扩展;如果OP使用的操作系统不使用GNU coreutils版本,那就不起作用了。(当然,这些文件需要在join字段中进行排序,它们在OP的示例中。)kvantour,惊人的解决方案一个解释多谢了,OP说她有“许多文件”这5只是一个例子,所以也许你可以展示一个脚本来概括N个文件的处理过程?太好了,谢谢,我认为这会更有用。多维数组是GNUextension@oguzismail说得对。这只是另一种方式。我一点也不赞同你的观点:)sjsam,tks很多
$ cat tst.awk
FNR==1 { numCols = colNr }
{
key = $1
for (i=2; i<=NF; i++) {
colNr = numCols + i - 1
val = $i
lgth = length(val)
vals[key][colNr] = val
wids[colNr] = (lgth > wids[colNr] ? lgth : wids[colNr])
}
}
END {
numCols = colNr
PROCINFO["sorted_in"] = "@ind_num_asc"
for (key in vals) {
printf "%s", key
for (colNr=1; colNr<=numCols; colNr++) {
printf "%s%*d", OFS, wids[colNr], vals[key][colNr]
}
print ""
}
}
$ awk -f tst.awk file*
1001 1 2 0 5 0 102
1002 1 2 7 3 10 0
1003 3 5 8 0 0 305
1004 6 7 0 0 60 0
1005 8 9 0 0 0 809
1007 0 0 0 0 4 0
1009 2 3 0 0 0 0