У меня есть несколько файлов входных данных ascii с тремя столбцами, например, так:

File1 :

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

File2 :

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

File3 :

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

Однако это дало бы мне выходной файл с «дырами» с выводом, похожим на:

Выход:

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).

0
Ben 4 Апр 2017 в 01:54

2 ответа

Лучший ответ

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}

ОБНОВЛЕНИЕ Первое решение не всегда лучшее, слишком много повторений кода и недостаточно общее. Следующий лучше, но не обязательно короче. Но может быть расширен до большего количества файлов.

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}
0
karakfa 4 Апр 2017 в 01:19

И 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]
    }
}

Баш (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 файлами

0
grail 4 Апр 2017 в 04:56