Se eu tiver dois arquivos (com colunas únicas), um assim (arquivo1)
34
67
89
92
102
180
blue2
3454
E o segundo arquivo (arquivo2)
23
56
67
69
102
200
Como encontro elementos comuns nos dois arquivos (interseção)? A saída esperada neste exemplo é
67
102
Observe que o número de itens (linhas) em cada arquivo é diferente. Números e strings podem ser misturados. Eles podem não ser necessariamente classificados. Cada item aparece apenas uma vez.
ATUALIZAR:
Verificação de tempo com base em algumas das respostas abaixo.
# generate some data
>shuf -n2000000 -i1-2352452 > file1
>shuf -n2000000 -i1-2352452 > file2
#@ilkkachu
>time (join <(sort "file1") <(sort "file2") > out1)
real 0m15.391s
user 0m14.896s
sys 0m0.205s
>head out1
1
10
100
1000
1000001
#@Hauke
>time (grep -Fxf "file1" "file2" > out2)
real 0m7.652s
user 0m7.131s
sys 0m0.316s
>head out2
1047867
872652
1370463
189072
1807745
#@Roman
>time (comm -12 <(sort "file1") <(sort "file2") > out3)
real 0m13.533s
user 0m13.140s
sys 0m0.195s
>head out3
1
10
100
1000
1000001
#@ilkkachu
>time (awk 'NR==FNR { lines[$0]=1; next } $0 in lines' "file1" "file2" > out4)
real 0m4.587s
user 0m4.262s
sys 0m0.195s
>head out4
1047867
872652
1370463
189072
1807745
#@Cyrus
>time (sort file1 file2 | uniq -d > out8)
real 0m16.106s
user 0m15.629s
sys 0m0.225s
>head out8
1
10
100
1000
1000001
#@Sundeep
>time (awk 'BEGIN{while( (getline k < "file1")>0 ){a[k]}} $0 in a' file2 > out5)
real 0m4.213s
user 0m3.936s
sys 0m0.179s
>head out5
1047867
872652
1370463
189072
1807745
#@Sundeep
>time (perl -ne 'BEGIN{ $h{$_}=1 while <STDIN> } print if $h{$_}' <file1 file2 > out6)
real 0m3.467s
user 0m3.180s
sys 0m0.175s
>head out6
1047867
872652
1370463
189072
1807745
A versão perl foi a mais rápida seguida pela awk. Todos os arquivos de saída tinham o mesmo número de linhas.
Para fins de comparação, classifiquei a saída numericamente para que seja idêntica.
#@ilkkachu
>time (join <(sort "file1") <(sort "file2") | sort -k1n > out1)
real 0m17.953s
user 0m5.306s
sys 0m0.138s
#@Hauke
>time (grep -Fxf "file1" "file2" | sort -k1n > out2)
real 0m12.477s
user 0m11.725s
sys 0m0.419s
#@Roman
>time (comm -12 <(sort "file1") <(sort "file2") | sort -k1n > out3)
real 0m16.273s
user 0m3.572s
sys 0m0.102s
#@ilkkachu
>time (awk 'NR==FNR { lines[$0]=1; next } $0 in lines' "file1" "file2" | sort -k1n > out4)
real 0m8.732s
user 0m8.320s
sys 0m0.261s
#@Cyrus
>time (sort file1 file2 | uniq -d > out8)
real 0m19.382s
user 0m18.726s
sys 0m0.295s
#@Sundeep
>time (awk 'BEGIN{while( (getline k < "file1")>0 ){a[k]}} $0 in a' file2 | sort -k1n > out5)
real 0m8.758s
user 0m8.315s
sys 0m0.255s
#@Sundeep
>time (perl -ne 'BEGIN{ $h{$_}=1 while <STDIN> } print if $h{$_}' <file1 file2 | sort -k1n > out6)
real 0m7.732s
user 0m7.300s
sys 0m0.310s
>head out1
1
2
3
4
5
Todas as saídas agora são idênticas.
Simples
comm
+sort
solução:-12
- suprimir a coluna1
e2
(linhas exclusivas paraFILE1
eFILE2
respectivamente), gerando apenas linhas comuns (que aparecem em ambos os arquivos)Em
awk
, isso carrega o primeiro arquivo totalmente na memória:Ou, se você quiser acompanhar quantas vezes uma determinada linha aparece:
join
poderia fazer isso, embora exija que os arquivos de entrada sejam classificados, então você precisa fazer isso primeiro e, ao fazê-lo, perde a ordem original:estranho
Essa é a boa solução porque (para arquivos grandes) deve ser a mais rápida, pois omite a impressão da mesma entrada mais de uma vez e a verificação de uma entrada novamente após a correspondência.
grep
Isso geraria a mesma entrada várias vezes se ocorresse mais de uma vez em
file2
.ordenar
Para se divertir (deve ser muito mais lento que
grep
):Com GNU uniq:
Resultado:
versão ligeiramente diferente e versão
awk
equivalenteperl
tempo relatado para três execuções consecutivas