Usando awk para escribir solo duplicados

Usando awk para escribir solo duplicados

Eliminar duplicados con awk es bastante común y sencillo. Pero necesito imprimir solo aquellas líneas que están duplicadas cuando comparamos solo una columna. Probé este comando:

awk 'seen[$2]++'

pero como puedes ver tiene defectos. Imprime duplicados, pero sólo desde su segunda aparición. Recién comencé a acostumbrarme a Unix y bash, por lo que sería genial si pudieras explicarme la solución.

Respuesta1

Puedo ver 2 formas de hacer esto:

  1. iterar sobre el archivo dos veces:

    En la primera iteración, cuente el número de veces que aparece cada $2.
    En la segunda iteración, imprima solo las líneas donde el recuento sea superior a 1

    awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
    
  2. con una sola iteración de los datos:

    Necesitas contar el número de veces que aparece cada $2,yRecuerde qué líneas se produjeron por cada $2.

    Esta respuesta usa GNU awk para matrices de matrices. No es probable que el orden de salida sea el mismo que el de los datos de entrada. También tiene que almacenar el archivo completo en la memoria.

    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
    

Probado con archivo de entrada:

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

y resultado esperado

a b
b b
c b
a d
b d

Respuesta2

Usando la misma entrada de muestra quejackmanla respuesta

$ 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]++si $2no se encuentra antes:
    • fl[$2]=$0guarde esta primera línea, asumí que la entrada no está ordenada y que pueden ocurrir duplicados en cualquier parte del archivo, por lo tanto, guárdela en función $2 de una variable temporal en lugar de solo
    • c[$2]=1de manera similar, inicialice la variable de recuento con 1
  • $2 in seensi $2ha ocurrido antes:
    • if(c[$2]--){print fl[$2]}Primero imprima la línea anterior, el contador disminuye para que la condición falle en coincidencias posteriores.
    • printluego imprima la línea actual


Con alguna otra 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

Tenga en cuenta que el orden depende de cómo se producen los duplicados.

Respuesta3

Cuando itera sobre el mismo archivo dos veces, puede utilizar números de línea como índices convenientes; puede generar una lógica más limpia.

awk 'NR == FNR {if ($2 in z) { y[z[$2]]; y[FNR] } z[$2]=FNR; next} (FNR in y)' file file

Utilicé un truco similar en mi respuesta a esta pregunta:


La base de este truco es que Awk creará una variable simplemente haciendo referencia a ella, y la index in arraynameconstrucción devuelve verdadero o falso dependiendo de si se ha creado un elemento de matriz con el índice especificado.

información relacionada