1

我想编写一个 bash 代码,以便按以下方式比较两个不同的文件。当两个文件中前五列的值相等时,将两个不同文件中剩余列的值相加(例如colum6file1+colum6file2等)。当它们不同时,只需将两个不同的行都保留在新文件中。请注意,我无法逐行比较,因为第一个文件开头的一行可以与第二个文件末尾的一行具有相同的四列值。

希望这很清楚,

谢谢。

文件1:

  118    2   AA      4.25     30.25  1  
  119    2   AA      4.50     30.25  2  
  120    2   AA      4.75     30.25  3  
  121    2   AA      5.00     30.25  4  
  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  

文件2:

  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   
  118    2   AA      4.25     30.25  6   
  119    2   AA      4.50     30.25  7  
  120    2   AA      5.00     30.25  8   
  121    2   AA      5.00     30.25  9   

输出:

  118    2   AA      4.25     30.25  7    
  119    2   AA      4.50     30.25  9    
  120    2   AA      4.75     30.25  11    
  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.50     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   
4

3 回答 3

2

这可能会被最小化,但这里有一个粗略的解决方案来解决我认为的问题:

sort file1 file2 |
  awk '{current = sprintf("%s %s %s %s %s", $1, $2, $3, $4, $5)

      if (current == previous) {
        total = total + $6
      } else if (previous) {
        printf("%s %d\n", previous, total)
        total = $6
      } else {
        total = $6
      }

      previous = current
    }
    END { printf("%s %d\n", previous, total) }'
于 2012-06-14T14:52:58.123 回答
0

您可以使用该sed命令仅获取第一列,然后进行比较。

sed 教程

所以:sed -e "s/([0-9]*\)\s.*/\1/"

将获得第一个数字(由 - 空白字符与其他数字分隔\s)基本上看起来像s/match pattern/replacement/.

您用于/( /)标记某些文本或模式的出现并\<number>在替换字符串中使用它。作为替换字符串,我使用\1它是第一个(也是唯一一个)标记的片段。

正如我在这里使用的那样:我找到了第一个数字 ( [0-9]*) 并标记了它,用空格 ( \s) 分隔并替换了我输入的其余行.*

于 2012-06-14T14:59:32.410 回答
0

这是一个解决方案。我不确定它是优雅的还是糟糕的。它利用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
于 2012-06-14T18:21:37.027 回答