Vamos analisar ferramentas que combinam dois arquivos linha a linha de alguma forma:
- colar combina dois arquivos linha por linha, sem prestar atenção a o conteúdo.
- comm combina arquivos classificados, prestando atenção a linhas idênticas. Isso pode eliminar linhas idênticas, mas a combinação posterior da linha diferente exigiria uma ferramenta diferente.
- join combina arquivos classificados, combinando campos idênticos juntos.
- classificar pode mesclar dois arquivos. O
- awk pode combinar vários arquivos de acordo com as regras que você der. Mas com arquivos tão grandes, você provavelmente obterá o melhor desempenho usando as ferramentas de finalidade especial mais adequadas do que com ferramentas generalistas.
Assumirei que não há duplicados, ou seja, dentro de um arquivo não há duas linhas com o mesmo ID, data e recurso. Se houver duplicatas, então, como lidar com elas depende de como você deseja tratá-las. Eu também suponho que os arquivos estão classificados. Eu também presumo que seu shell tem substituição de processo , por exemplo bash ou ksh ao invés de simples sh, e que você tem GNU coreutils (que é o caso de Linux e Cygwin não-embarcados).
Não sei se seus separadores são espaços em branco ou guias. Eu assumirei o espaço em branco; se o separador for sempre exatamente uma guia, declarar a guia como o caractere separador ( cut -d $'\t'
, join -t $'\t'
, sort -t $'\t'
) e usar \ t em vez de [ \t]\+
deverá reduzir um pouco o desempenho.
Defina a localidade como puro ASCII ( LC_ALL=C
) para evitar qualquer perda de desempenho relacionada a caracteres multibyte.
Como join
só pode combinar linhas com base em um campo, precisamos organizar os campos 1 a 3 para aparecer como um único campo. Para fazer isso, altere o separador, entre 1 e 2 e 2 e 3 ou entre 3 e 4. Vou mudar de 1 a 3 para usar ;
em vez de espaço em branco. Dessa forma, você obtém todas as combinações de linhas, sejam elas idênticas ou não. Você pode então usar sed para remover linhas com valores idênticos.
join -a 1 -a 2 <(sed 's/[ \t]\+/;/; s/[ \t]\+/;/' file1.csv) <(sed 's/[ \t]\+/;/; s/[ \t]\+/;/' file2.csv) |
sed '/[ \t]\(.*\)[ \t]\+$/d' |
tr ';' '\t'
Observe que as linhas não pareadas acabam sendo uma linha de quatro colunas sem indicação se vieram do arquivo 1 ou arquivo 2. Remova -a 1 -a 2
para suprimir todas as linhas não parciais.
Se você tem uma maioria de linhas idênticas, isso desperdiça tempo juntando-as e eliminando-as. Outra abordagem seria usar comm -3
para eliminar as linhas idênticas. Isso produz um único fluxo de saída onde as linhas estão em ordem, mas as linhas do arquivo 2 têm uma guia principal. Você pode então usar o awk para combinar linhas consecutivas onde os dois arquivos têm os mesmos campos 1–3. Como isso envolve o awk, pode acabar sendo mais lento se houver muitas linhas não idênticas.
comm -3 file1.csv file2.csv |
awk '
$1 "\t" $2 "\t" $3 == k { if ($4 != v) print k "\t" v "\t" $4; next; }
{ print k "\t" v }
{ k=$1 "\t" $2 "\t" $3; v=$4; }
'