这是一个解决方案。我不确定它是优雅的还是糟糕的。它利用join
命令(因此也利用sort
命令),加上awk
.
file1=xx1
file2=xx2
join -t ' ' -j 1 -a 1 -a 2 -o 1.1,2.1,1.2,2.2 \
<(sort -b $file1 | awk '{printf("%s\t%s\t%s\t%s\t%s %s\n",$1,$2,$3,$4,$5,$6)}') \
<(sort -b $file2 | awk '{printf("%s\t%s\t%s\t%s\t%s %s\n",$1,$2,$3,$4,$5,$6)}') |
awk '{
if (NF == 6) sum = $6;
else sum = $11 + $12;
print $1, $2, $3, $4, $5, sum;
}'
文件名是参数。<(sort -b $fileN | awk '...')
是“过程替代”,这是bash
. 这意味着'运行括号内的命令,并且管道的标准输出作为命名文件(通常/dev/fd/NN
)提供给命令,在本例中为join
. 管道是迂回的;他们对数据进行排序,然后awk
格式化数据,以便关键字段(前 5 个字段)由制表符分隔,但第六个字段与它们之间用空格分隔。忽略字段中的sort -b
前导空格;获得理智的结果至关重要。
该join
命令将空白视为字段分隔符 ( -t ' '
),因此就所涉及的而言,每个输入文件都有两个字段join
。连接发生在第一个字段相等时-j 1
(记住第一个字段是 5 个制表符分隔值的集合)。输出包括文件 1 ( -a 1
) 和文件 2 ( -a 2
) 中不匹配的条目。输出格式为文件 1 中的字段 1、文件 2 中的字段 1、文件 1 中的字段 2 和文件 2 中的字段 2。
如果匹配,则输出将有十几个空格或制表符分隔的列。如果没有匹配,那么输出将有半打空白或制表符分隔的列。
中的后处理awk
利用了空格和制表符都是字段分隔符的事实awk
。如果输入行有 6 个字段,则该键不匹配,因此总和是 中的值$6
。如果输入行有任何其他数量的字段(应该是 12),那么总和就是最后两个字段的值,$11 + $12
. 然后脚本打印出前五个字段,加上总和。
这种将空格与制表符作为字段分隔符的诡计可能很奇怪,但在这种情况下它相当有效。
由于样本数据中每个文件中最多有一行具有给定键(每个文件中的前五列是唯一的),因此join
将文件 1 中的 N 行与文件 2 中的 M 行配对以提供 NxM 输出行无关紧要,因为 N = 1 和 M = 1 根据需要。
示例输出
给定问题中显示的数据文件(以及文件中的脚本jb.sh
):
$ bash jb.sh
118 2 AA 4.25 30.25 7
119 2 AA 4.50 30.25 9
120 2 AA 4.75 30.25 3
120 2 AA 5.00 30.25 8
121 2 AA 5.00 30.25 13
122 2 AA 5.25 30.25 5
123 2 AA 5.50 30.25 6
124 2 AA 5.75 30.25 7
125 2 AA 6.00 30.25 8
126 2 AA 6.25 30.25 9
179 1 BB 19.75 30.00 1
180 1 BB 19.75 30.00 2
230 1 BB 32.25 30.00 3
231 1 BB 32.50 30.00 4
232 1 BB 32.75 30.00 5
$
我注意到这个输出与期望的输出不同,但那是因为期望的输出对于给定的输入是错误的(或者输入对于期望的输出是错误的)。样本数据有以下几行:
120 2 AA 4.75 30.25 3
120 2 AA 5.00 30.25 8
请注意,此处的第 4 列不一样(4.75 与 5.00),因此这些行无法合并,您会得到两个输出行“120”。
扩展到 16 列
该脚本进行了少量整理,否则将继续与以前相同的模式:
file1=xx1
file2=xx2
sort_print()
{
sort -b "$@" |
awk '{printf("%s\t%s\t%s\t%s\t%s %s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n",
$1,$2,$3,$4,$5, $6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)}'
}
join -t ' ' -j 1 -a 1 -a 2 -o 1.1,2.1,1.2,2.2 \
<(sort_print $file1) <(sort_print $file2) |
tee joined.data |
awk '{
if (NF != 16)
{
for (i = 6; i <= 16; i++)
{
j = i + 16
$i = $i + $j;
}
}
printf "%s %s %s %s %s", $1, $2, $3, $4, $5;
for (i = 6; i <= 16; i++)
printf " %s", $i;
printf "\n";
}'
档案 xx1
118 2 AA 4.25 30.25 1 2 3 4 5 14 5 4 5 7 8
119 2 AA 4.50 30.25 2 2 3 4 5 4 15 4 5 7 8
120 2 AA 5.00 30.25 3 2 3 4 5 4 5 14 5 7 8
121 2 AA 5.00 30.25 4 2 3 4 5 4 5 4 15 7 8
122 2 AA 5.25 30.25 5 2 3 4 5 4 5 4 5 17 8
123 2 AA 5.50 30.25 6 2 13 4 5 4 5 4 5 7 18
124 2 AA 5.75 30.25 7 2 3 4 5 4 5 4 5 7 18
125 2 AA 6.00 30.25 8 12 3 4 5 4 5 4 5 7 18
126 2 AA 6.25 30.25 9 2 3 4 5 4 5 4 5 7 18
档案 xx2
179 1 BB 19.75 30.00 1 8 21 2 4 12 2 3 1 9 1032
180 1 BB 19.75 30.00 2 29 1 22 4 12 2 3 1 9 1042
230 1 BB 32.25 30.00 3 9 8 2 44 12 2 3 1 9 1052
231 1 BB 32.50 30.00 4 9 17 2 44 12 2 3 1 9 1062
232 1 BB 32.75 30.00 5 9 8 1 4 122 2 3 1 9 1072
118 2 AA 4.25 30.25 6 9 8 1 2 4 22 3 1 9 1082
119 2 AA 4.50 30.25 7 29 8 1 2 4 12 23 1 9 1092
120 2 AA 5.00 30.25 8 9 8 1 2 4 12 2 21 9 1002
121 2 AA 5.00 30.25 9 9 8 1 2 4 12 2 3 29 1012
输出
118 2 AA 4.25 30.25 124 11 8 5.25 32.25 5 24 6 5 14 1096
119 2 AA 4.50 30.25 126 31 8 5.5 32.25 6 14 26 5 14 1096
120 2 AA 5.00 30.25 128 11 8 6 32.25 7 14 5 25 14 1006
121 2 AA 5.00 30.25 130 11 8 6 32.25 8 14 5 7 34 1016
122 2 AA 5.25 30.25 5 2 3 4 5 4 5 4 5 17 8
123 2 AA 5.50 30.25 6 2 13 4 5 4 5 4 5 7 18
124 2 AA 5.75 30.25 7 2 3 4 5 4 5 4 5 7 18
125 2 AA 6.00 30.25 8 12 3 4 5 4 5 4 5 7 18
126 2 AA 6.25 30.25 9 2 3 4 5 4 5 4 5 7 18
179 1 BB 19.75 30.00 1 8 21 2 4 12 2 3 1 9 1032
180 1 BB 19.75 30.00 2 29 1 22 4 12 2 3 1 9 1042
230 1 BB 32.25 30.00 3 9 8 2 44 12 2 3 1 9 1052
231 1 BB 32.50 30.00 4 9 17 2 44 12 2 3 1 9 1062
232 1 BB 32.75 30.00 5 9 8 1 4 122 2 3 1 9 1072