Como combinar o conteúdo de dois arquivos com o mesmo valor em uma coluna?

3

Arquivo1:

00:00 274
00:04 476
00:05 450
00:06 499
00:07 373
00:08 206
00:09 471
00:10 154

Arquivo2:

00:00 183
00:01 60
00:02 344
00:03 540
00:04 450
00:07 348
00:09 473
00:10 203

Saída desejada:

00:00,274,183
00:01,0,60
00:02,0,344
00:03,0,540
00:04,476,450
00:05,450,0
00:06,499,0
00:07,373,348
00:08,206,0
00:09,471,473
00:10,154,203

A coluna 1 de cada arquivo será verificada e, se for o mesmo, os valores serão unidos à saída. Por favor, observe o valor "0" para aqueles conteúdos que não estão presentes em nenhum arquivo. Além disso, isso será usado para combinar o conteúdo de 6 arquivos.

    
por binoy_php 28.01.2014 / 09:13

6 respostas

5

Supondo que os arquivos de entrada são classificados em ordem alfabética no campo de junção (como estão em sua amostra):

join -e0 -a1 -a2 -o 0,1.2,2.2 file1 file2 | tr ' ' ,
    
por 28.01.2014 / 14:08
3

Bash

#!/bin/bash 
file1=t1
file2=t2
while  read line
do
        v1=$(grep "${line}" $file1|| echo 0)
        v2=$(grep "${line}" $file2|| echo 0)
        echo ${line},${v1#* },${v2#* }

done < <(awk '!a[$1]++{print $1| "sort"}' $file1 $file2)

Resultado

00:00,274,183
00:01,0,60
00:02,0,344
00:03,0,540
00:04,476,450
00:05,450,0
00:06,499,0
00:07,373,348
00:08,206,0
00:09,471,473
00:10,154,203
    
por 28.01.2014 / 13:41
2

Infelizmente join não adiciona os arquivos que estão faltando linhas. A opção -e apenas adiciona o argumento a linhas com a chave. Você pode extrair as chaves, adicioná-las a cada arquivo, se ainda não estiver lá com as ferramentas padrão, e então usar join, mas então é melhor escrever um programa pequeno, por exemplo, em Python:

import sys

default = ['0'] * len(sys.argv[1:])
r = {}
for idx, fn in enumerate(sys.argv[1:]):
    for line in open(fn):
        c1, c2 = line.split()
        r.setdefault(c1, default[:])[idx] = c2
for c1 in sorted(r): # print output
    print("{},{}".format(c1, ','.join(r[c1])))

salve como join.py e execute com

python join.py file1 file2 [file3 ....]

i.e. você pode adicionar quantos arquivos você tiver na linha de comando

Isso dá exatamente a saída que você solicitou (exceto que você trocou valores por 00:02 e 00:03)

    
por 28.01.2014 / 10:19
2

Se a ordem das linhas não é importante, ou você não se importa em ter a entrada e a saída classificadas (o que não parece ser um problema considerando a entrada que você deu), você pode usar join duas vezes:

(
    join -a 1 -e "0" -o "1.1 1.2 2.2" file1 file2
    join -a 2 -e "0" -o "2.1 1.2 2.2" file1 file2
) | sort -u | sed "s/ /,/g"

A opção -a reproduz linhas não correspondentes do primeiro ( -a1 ) ou do segundo ( -a2 ), -e "0" usa zero como um substituto para as linhas ausentes do outro arquivo, -o descreve o formato das linhas de saída como uma lista de valores FILE.FIELD (consulte join(1) man page). sort -u remove duplicatas de linhas. O% final sed substitui todos espaços em cada linha por vírgulas.

Ou, se você for bastante aventureiro, descobrirá que uma vez é suficiente com as opções certas . Obrigado Stephane!

    
por 28.01.2014 / 11:36
2

Editado

Versão mais simples.

Script atualizado: test2.awk

FNR==NR{ a[$1]=$2;next; }
{ 
    if ($1 in a){ 
        a[$1] = ( $1 "," a[$1] "," $2 )
    }else{
        a[$1] = ( $1 ",0," $2 )
    }
} END {
    for ( x in a ){
        if ( match(a[x],x) ){print a[x]}else{ print x "," a[x] ",0"}
    }
}

Linha de comando

awk -f new.awk 1.txt 2.txt | sort

Primeira tentativa original

Aqui está o awk. Não tenho certeza de como classificar rapidamente uma matriz associativa, apenas canalize-a para classificar. Trabalho.

script test.awk

BEGIN{st=0}
{if(st==0){
    cur=FILENAME; st++} 
 if((st==1)&&(cur==FILENAME)){ 
     a[$1]=$2; 
 }
 else{ b[$1]=$2 } 
}END{ 
    for(i in b){ 
        if(a[i]){ 
            a[i]=a[i] "," b[i]; 
        }else{ a[i]="0," b[i] } }
    for(i in a){ 
        if (b[i]){
            print i "," a[i] 
        }else{ 
            print i "," a[i] ",0" 
        }
    }
}

cmdline

awk -f test.awk 1.txt 2.txt | sort

saída

00:00,274,183
00:01,0,60
00:02,0,344
00:03,0,540
00:04,476,450
00:05,450,0
00:06,499,0
00:07,373,348
00:08,206,0
00:09,471,473
00:10,154,203
    
por 28.01.2014 / 11:54
0

Experimente o seguinte script Perl

#!/usr/bin/perl

$filenum = $#ARGV;
if ($filenum < 0) {
    print "No arguments\n";
    exit(1);
}

for (my $i=0; $i<=$filenum; $i++) {
    open($fh,"<","$ARGV[$i]") || die "Could not open $ARGV[$i]\n";

    while (<$fh>) {
        ($a,$b) = split/\s/;
        @{$myhash{$a}}[$i]=$b;
    }
    close($fh);
}

foreach my $x ( sort keys %myhash) {
    print "$x";
    for (my $i=0; $i<=$filenum; $i++) {
        if (defined @{$myhash{$x}}[$i]) {
            print ",@{$myhash{$x}}[$i]";
        }
        else {
            print ",0";
        }
    }
    print "\n";
}

salve-o em um arquivo myscript.pl e execute-o como:

perl myscript.pl file1 file2 ...

Espero que ajude.

    
por 28.01.2014 / 09:45