Usando awk para escrever apenas duplicatas

Usando awk para escrever apenas duplicatas

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:

  1. 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 1

    awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
    
  2. 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 $2não for encontrado antes:
    • fl[$2]=$0salve 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ária
    • c[$2]=1da mesma forma, inicialize a variável de contagem com 1
  • $2 in seense $2já 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 subsequentes
    • printentã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 arraynameconstrução retornará verdadeiro ou falso dependendo se um elemento do array foi criado com o índice especificado.

informação relacionada