Usando o awk para gravar apenas duplicatas

3

Excluir duplicatas com o awk é bastante comum e simples. Mas preciso imprimir apenas as linhas duplicadas quando comparamos apenas uma coluna. Eu tentei este comando:

awk 'seen[$2]++'

mas como você pode ver, tem falhas. Imprime duplicatas, mas apenas desde a sua segunda aparição. Eu só comecei a me acostumar com o unix e o bash, então seria ótimo se você pudesse explicar a solução para mim.

    
por ogarogar 04.11.2016 / 17:07

3 respostas

1

Eu vejo duas maneiras de fazer isso:

  1. iterar o arquivo duas vezes:

    Na primeira iteração, conte o número de vezes que cada $ 2 aparece.
    Na segunda iteração, imprima apenas 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, e lembrar quais linhas ocorreram para cada $ 2.

    Esta resposta usa o GNU awk para array-of-arrays. A ordem da saída provavelmente não será a mesma dos dados de entrada. Também tem que 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 o arquivo de entrada:

$ cat file
a b
b b
c b
a c
a d
b d
a e

e saída esperada

a b
b b
c b
a d
b d
    
por 04.11.2016 / 17:34
1

Usando a mesma entrada de amostra da resposta do de glenn

$ 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 essa primeira linha, assumi que a entrada não está classificada e as duplicatas podem ocorrer em qualquer lugar do arquivo, portanto, salvando-a com base no $2 , em vez de apenas na variável temporária
    • c[$2]=1 da mesma forma, inicialize a variável count com 1
  • $2 in seen if $2 ocorreu antes:
    • if(c[$2]--){print fl[$2]} primeiro imprime a linha anterior, o contador é diminuído para que a condição falhe para correspondências subseqüentes
    • print , em seguida, imprime 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 as duplicatas ocorrem

    
por 04.11.2016 / 17:39
0

Quando você faz iterações sobre o mesmo arquivo duas vezes, pode usar números de linha como índices convenientes; pode fazer 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

Eu usei um truque semelhante na minha resposta a esta pergunta:

A base deste truque é que o Awk criará uma variável simplesmente referenciando-a, e a construção index in arrayname retornará verdadeiro ou falso, dependendo de se um elemento da matriz foi criado com o índice especificado.

    
por 05.11.2016 / 05:51

Tags