comm e join: Comparando e Unindo Arquivos
O Que Voce Vai Aprender
- Como extrair linhas comuns e diferencas entre dois arquivos com
comm - Como unir dois arquivos horizontalmente por uma chave compartilhada com
join - Como evitar falhas relacionadas a ordenacao como linhas ausentes ou avisos
not sorted
Resumo Rapido
- Quer linhas comuns / diferencas linha por linha -->
comm - Quer unir duas tabelas por uma coluna chave (como SQL JOIN) -->
join - Ambos exigem entrada ordenada -- isso e inegociavel
Premissas (ambiente)
- GNU coreutils (Ubuntu / maioria das distribuicoes Linux)
commejoinvem com o coreutils. Nenhuma instalacao extra necessaria
O que e comm e o que ele faz?
Conclusao: comm compara dois arquivos ordenados linha por linha e imprime "somente esquerdo / somente direito / comum" em tres colunas.
comm compara dois arquivos ordenados e organiza o resultado em tres colunas.
- Coluna 1: linhas somente no arquivo1
- Coluna 2: linhas somente no arquivo2
- Coluna 3: linhas em ambos (linhas comuns)
Aqui estao 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 posicao da coluna e indicada pela indentacao de tab. apple esta somente no esquerdo (coluna 1), banana e cherry estao em ambos (coluna 3, dois tabs), e date esta somente no direito (coluna 2, um tab).
Por que comm exige entrada ordenada?
Conclusao: comm usa um merge simples que le ambos os arquivos linha por linha, entao entrada desordenada faz com que ele perca linhas comuns.
comm avanca por ambos os arquivos simultaneamente a partir do topo. Se a ordem estiver quebrada, ele nao consegue detectar linhas correspondentes corretamente e a saida fica incorreta. Entrada desordenada dispara este aviso:
comm: file 1 is not in sorted order
Sempre execute sort primeiro. A substituicao de processo evita arquivos temporarios:
$ comm <(sort a.txt) <(sort b.txt)
A colacao do sort e dependente do locale. Se comm e sort discordarem na ordenacao, os resultados falham. Em caso de duvida, fixe a ordem com LC_ALL=C sort para estabilidade.
Como selecionar colunas especificas do comm?
Conclusao: Use -1, -2, -3 para suprimir a coluna correspondente. O numero refere-se a coluna que voce remove.
As opcoes especificam a coluna a suprimir, nao a coluna a exibir.
| Objetivo | Comando | Coluna restante |
|---|---|---|
| Somente linhas comuns | comm -12 a b |
Coluna 3 |
| Somente diferencas | 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 memorizacao: os numeros dizem "remova estas colunas." Para manter somente linhas comuns, remova as colunas 1 e 2 com -12.
O que e join e como e diferente de comm?
Conclusao: 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 unica linha. A chave padrao e 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, entao sao unidas. 3 carol e 4 marketing nao tem correspondencia e nao sao impressos (inner join).
join tambem 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 juncao e o delimitador?
Conclusao: Use -t para o delimitador, -1 / -2 para a coluna chave de cada arquivo, e -o para os campos de saida.
Ajuste estes quando o delimitador for diferente (como CSV) ou a chave nao 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 e a coluna 1; em depts.csv a chave e a coluna 2. O delimitador e virgula.
$ join -t, -1 1 -2 2 users.csv depts.csv
1,alice,tokyo,sales 2,bob,osaka,engineering
-t,: define o delimitador como virgula-1 1: a chave no arquivo1 e a coluna 1-2 2: a chave no arquivo2 e a coluna 2
Use -o para especificar os campos de saida. -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 correspondencia (outer join)?
Conclusao: Use -a para tambem imprimir linhas sem par. -a 1 e um left outer join; -a 1 -a 2 e um full outer join.
Um inner join descarta linhas sem correspondencia. Use -a para mante-las.
$ join -a 1 users.txt depts.txt
1 alice sales 2 bob engineering 3 carol
3 carol nao tem correspondencia em depts.txt, mas -a 1 a mantem (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 saida.