索引匹配多个文件

时间:2017-04-03 22:54:49

标签: bash multiple-files

我有多个ascii输入数据文件,其中包含三列,如下所示:

File1中:

00005 3  a
00005 17 b
00007 20 c
00009 2  d
00042 4  e
00042 37 f
00090 49 g

文件2:

00005 3  A
00005 17 B
00009 2  C
00007 20 D
00042 4  E
00090 49 F
00042 37 G

文件3:

00005 3  100
00009 2  200
00007 20 300
00090 49 400
00042 37 500

前两列的作用类似于索引,第三列是数据属性。从我的示例文件中可以看出,前两列不必是任何顺序,某些文件中可能缺少某些索引。我想比较所有三个文件并输出如下:

输出1(合并数据):

00005 3  a  A  100
00007 20 c  D  300
00009 2  d  C  200
00042 37 f  G  500
00090 49 g  F  400

输出2(数据不完整的指标):

00005 17
00042 4

我当前(和草率)的解决方案包括查找具有最多行的文件,从中获取索引,在其他文件中查找索引,以及打印这些:

我的解决方案:

cat file1 | while read line
do
  index1=$(echo $line | awk '{print $1}')
  index2=$(echo $line | awk '{print $2}')
  attribute1=$(echo $line | awk '{print $3}')
  attribute2=$(grep "^"$index1" "$index2" " file2 | awk '{print $3}')
  attribute3=$(grep "^"$index1" "$index2" " file3 | awk '{print $3}')
  echo $index1 $index2 $attribute1 $attribute2 $attribute3
done > output

然而,这会给我一个带有'holes'的输出文件,输出看起来像:

输出:

00005 3  a  A  100
00005 17 b  B 
00007 20 c  D  300
00009 2  d  C  200
00042 4  e  E
00042 37 f  G  500
00090 49 g  F  400

我仍然可以通过使用awk(分别为NF == 3和NF <3)找到好数据和缺失数据,但我觉得应该有一种更清洁(也可能更快)的方式我的解决方案很慢并容易出错(尤其是grep发现)。

2 个答案:

答案 0 :(得分:0)

awk救援!

如果你不能对文件进行排序,这里有一个解决方案

awk                 '{k=$1 FS $2} 
  FILENAME==ARGV[1]  {a[k]=$3; next} 
  FILENAME==ARGV[2]  {b[k]=$3; next} 
                     {c[k]=$3} 
(k in a) && (k in b) {print k,a[k],b[k],c[k] > "output1.txt"} 
                      delete a[k]; delete b[k]; delete c[k]} 
  END                {for(k in a) d[k]; 
                      for(k in b) d[k]; 
                      for(k in c) d[k]; 
                      for(k in d) print k > "output2.txt"}' file{1..3}

UPDATE 第一种解决方案并不总是最好的,代码重复太多而且不够通用。以下是更好的,但不一定更短。但可以扩展到更多的文件。

awk '{k=$1 FS $2} 
     {for(i=1;i<ARGC;i++) 
        if(FILENAME==ARGV[i]) 
           {a[k,i]=$3; c[k]++}} 
 END {f="output1.txt"; 
      for(k in c) 
         if(c[k]==ARGC-1) 
            {printf "%s", k > f; 
             for(i=1;i<=c[k];i++) printf "%s", OFS a[k,i] > f; 
             print "" > f} 
         else print k > "output2.txt"}' file{1..3}

答案 1 :(得分:0)

awk和bash都可以做到,但当然在讨论列时awk要容易得多:)

AWK:

#!/usr/bin/awk -f

{
    arr[$1][$2] = arr[$1][$2] (arr[$1][$2]?" ":"") $3
}
END{
    while(c++ < 2)
    {
        if( c == 1)
        {
            print "Combined values"
            reg = /[0-9]$/
        }
        else
        {
            print "Incomplete values"
            reg = /[A-Z]$/
        }

        for(i in arr)
            for(j in arr[i])
                if(arr[i][j] ~ reg)
                    print i,j,arr[i][j]
    }
}

bash(4 +):

#!/usr/bin/env bash

declare -A arr

for file
do
    while read -r i1 i2 v
    do
        arr[$i1$i2]="${arr[$i1$i2]}$([[ -n ${arr[$i1$i2]} ]] && echo -n " ")$v"
    done<"$file"
done

for i in 1 2
do
    if (( i == 1 ))
    then
        vals="Combined values"
        reg='[0-9]$'
    else
        vals="Incomplete values"
        reg='[A-Z]$'
    fi

    echo "$vals"

    for idx in "${!arr[@]}"
    do
        [[ "${arr[$idx]}" =~ $reg ]] && echo "${idx:0:5} ${idx:5} ${arr[$idx]}"
    done | column -t
done

您可以使用以下命令调用: - ./script_name files