
Excluir duplicatas com o awk é bastante comum e simples. Mas preciso imprimir apenas aquelas linhas que ficam duplicadas quando comparamos apenas uma coluna. Eu tentei este comando:
awk 'seen[$2]++'
mas como você pode ver, tem falhas. Imprime duplicatas, mas apenas a partir da segunda aparição. Eu apenas comecei a me acostumar com Unix e Bash, então seria ótimo se você pudesse me explicar a solução.
Responder1
Posso ver duas maneiras de fazer isso:
itere sobre o arquivo duas vezes:
Na primeira iteração, conte o número de vezes que cada $2 aparece.
Na segunda iteração, imprima apenas as linhas onde a contagem é maior que 1awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
com uma única iteração dos dados:
Você precisa contar o número de vezes que cada $2 aparece,elembre-se de quais linhas ocorreram para cada $2.
Esta resposta usa GNU awk para array de arrays. A ordem da saída provavelmente não será a mesma dos dados de entrada. Também deve armazenar o arquivo inteiro na memória.
gawk ' { lines[$2][++count[$2]] = $0 } END { for (x in lines) if (count[x] > 1) for (i=1; i<=count[x]; i++) print lines[x][i] } ' file
Testado com arquivo de entrada:
$ cat file
a b
b b
c b
a c
a d
b d
a e
e resultado esperado
a b
b b
c b
a d
b d
Responder2
Usando a mesma entrada de amostra queGlenn Jackmana resposta
$ awk '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' file
a b
b b
c b
a d
b d
!seen[$2]++
se$2
não for encontrado antes:fl[$2]=$0
salve esta primeira linha, presumi que a entrada não está classificada e duplicatas podem ocorrer em qualquer lugar do arquivo, salvando-a com base em$2
vez de apenas na variável temporáriac[$2]=1
da mesma forma, inicialize a variável de contagem com 1
$2 in seen
se$2
já ocorreu antes:if(c[$2]--){print fl[$2]}
primeiro imprima a linha anterior, o contador é decrementado para que a condição falhe nas correspondências subsequentesprint
então imprima a linha atual
Com alguma outra entrada
$ cat ip.txt
6.2 : 897 : bar
3.1 : 32 : foo
1.2 : 123 : xyz
2.3 : 32 : baz
7.5 : 897 : boo
$ awk -F: '$2 in seen{if(c[$2]--){print fl[$2]} print} !seen[$2]++{fl[$2]=$0; c[$2]=1}' ip.txt
3.1 : 32 : foo
2.3 : 32 : baz
6.2 : 897 : bar
7.5 : 897 : boo
Observe que a ordem depende de como ocorrem as duplicatas
Responder3
Ao iterar duas vezes no mesmo arquivo, você pode usar números de linha como índices convenientes; pode criar uma lógica mais limpa.
awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file
Usei um truque semelhante em minha resposta a esta pergunta:
A base desse truque é que o Awk criará uma variável simplesmente referenciando-a, e a index in arrayname
construção retornará verdadeiro ou falso dependendo se um elemento do array foi criado com o índice especificado.