
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:
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 1awk 'NR == FNR {count[$2]++; next} count[$2] > 1' file file
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$2
no se encuentra antes:fl[$2]=$0
guarde 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 soloc[$2]=1
de manera similar, inicialice la variable de recuento con 1
$2 in seen
si$2
ha 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.print
luego 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 arrayname
construcción devuelve verdadero o falso dependiendo de si se ha creado un elemento de matriz con el índice especificado.