comm e join: Comparando e Unindo Arquivos

comm e join: Comparando e Unindo Arquivos

O Que Você Vai Aprender

  • Como extrair linhas comuns e diferenças entre dois arquivos com comm
  • Como unir dois arquivos horizontalmente por uma chave compartilhada com join
  • Como evitar falhas relacionadas a ordenação como linhas ausentes ou avisos not sorted

Resumo Rápido

  • Quer linhas comuns / diferenças linha por linha --> comm
  • Quer unir duas tabelas por uma coluna chave (como SQL JOIN) --> join
  • Ambos exigem entrada ordenada -- isso é inegociável

Premissas (ambiente)

  • GNU coreutils (Ubuntu / maioria das distribuições Linux)
  • comm e join vêm com o coreutils. Nenhuma instalação extra necessária

O que é comm e o que ele faz?

Conclusão: comm compara dois arquivos ordenados linha por linha e imprime "somente esquerdo / somente direito / comum" em três colunas.

comm compara dois arquivos ordenados e organiza o resultado em três colunas.

  • Coluna 1: linhas somente no arquivo1
  • Coluna 2: linhas somente no arquivo2
  • Coluna 3: linhas em ambos (linhas comuns)

Aqui estão dois arquivos de exemplo:

$ cat a.txt
apple
banana
cherry

$ cat b.txt
banana
cherry
date
$ comm a.txt b.txt
apple
		banana
		cherry
	date

A posição da coluna é indicada pela indentação de tab. apple está somente no esquerdo (coluna 1), banana e cherry estão em ambos (coluna 3, dois tabs), e date está somente no direito (coluna 2, um tab).

Por que comm exige entrada ordenada?

Conclusão: comm usa um merge simples que lê ambos os arquivos linha por linha, então entrada desordenada faz com que ele perca linhas comuns.

comm avança por ambos os arquivos simultaneamente a partir do topo. Se a ordem estiver quebrada, ele não consegue detectar linhas correspondentes corretamente e a saída fica incorreta. Entrada desordenada dispara este aviso:

comm: file 1 is not in sorted order

Sempre execute sort primeiro. A substituição de processo evita arquivos temporários:

$ comm <(sort a.txt) <(sort b.txt)

A colação do sort é dependente do locale. Se comm e sort discordarem na ordenação, os resultados falham. Em caso de dúvida, fixe a ordem com LC_ALL=C sort para estabilidade.

Como selecionar colunas específicas do comm?

Conclusão: Use -1, -2, -3 para suprimir a coluna correspondente. O número refere-se à coluna que você remove.

As opções especificam a coluna a suprimir, não a coluna a exibir.

Objetivo Comando Coluna restante
Somente linhas comuns comm -12 a b Coluna 3
Somente diferenças comm -3 a b Colunas 1 e 2
Linhas somente no arquivo1 comm -23 a b Coluna 1
Linhas somente no arquivo2 comm -13 a b Coluna 2
$ comm -12 a.txt b.txt
banana
cherry
$ comm -23 a.txt b.txt
apple

Dica de memorização: os números dizem "remova estas colunas." Para manter somente linhas comuns, remova as colunas 1 e 2 com -12.

O que é join e como é diferente de comm?

Conclusão: join mescla linhas de dois arquivos por uma coluna chave compartilhada, equivalente a um INNER JOIN do SQL.

Enquanto comm compara linhas inteiras, join mescla linhas cujo campo chave corresponde em uma única linha. A chave padrão é o primeiro campo de cada linha.

$ cat users.txt
1 alice
2 bob
3 carol

$ cat depts.txt
1 sales
2 engineering
4 marketing
$ join users.txt depts.txt
1 alice sales
2 bob engineering

As chaves 1 e 2 existem em ambos os arquivos, então são unidas. 3 carol e 4 marketing não têm correspondência e não são impressos (inner join).

join também exige entrada ordenada pela coluna chave. Entrada desordenada produz um aviso join: ... is not sorted e descarta linhas. Assim como comm, execute sort primeiro.

Como definir campos de junção e o delimitador?

Conclusão: Use -t para o delimitador, -1 / -2 para a coluna chave de cada arquivo, e -o para os campos de saída.

Ajuste estes quando o delimitador for diferente (como CSV) ou a chave não for a primeira coluna.

$ cat users.csv
1,alice,tokyo
2,bob,osaka

$ cat depts.csv
sales,1
engineering,2

Em users.csv a chave é a coluna 1; em depts.csv a chave é a coluna 2. O delimitador é vírgula.

$ join -t, -1 1 -2 2 users.csv depts.csv
1,alice,tokyo,sales
2,bob,osaka,engineering
  • -t,: define o delimitador como vírgula
  • -1 1: a chave no arquivo1 é a coluna 1
  • -2 2: a chave no arquivo2 é a coluna 2

Use -o para especificar os campos de saída. -o 1.1,1.2,2.1 significa "colunas 1 e 2 do arquivo1, depois coluna 1 do arquivo2." Liste-os como <arquivo>.<campo>.

Como manter linhas sem correspondência (outer join)?

Conclusão: Use -a para também imprimir linhas sem par. -a 1 é um left outer join; -a 1 -a 2 é um full outer join.

Um inner join descarta linhas sem correspondência. Use -a para mantê-las.

$ join -a 1 users.txt depts.txt
1 alice sales
2 bob engineering
3 carol

3 carol não tem correspondência em depts.txt, mas -a 1 a mantém (com o campo ausente vazio). Para preencher a lacuna, combine -e e -o.

$ join -a 1 -e '-' -o '1.1,1.2,2.2' users.txt depts.txt
1 alice sales
2 bob engineering
3 carol -

-e '-' preenche campos ausentes com -, e -o fixa as colunas de saída.

comm vs join: qual usar

Conclusão: Use comm para diferenças de conjuntos de linhas e join para mesclagens baseadas em chave. Ambos exigem entrada ordenada.

Objetivo Comando
Encontrar linhas comuns / diferenças comm
Unir duas tabelas por chave comum join
Deduplicar / ordenar (pré-processamento) sort

Armadilhas a evitar

  • Passar entrada desordenada para comm / join (linhas são descartadas silenciosamente)
  • Misturar locales entre comm e sort (incompatibilidade de ordenação quebra resultados)
  • Esquecer as opções de coluna chave (-1 / -2) do join e usar o padrão coluna 1