Me gustaría eliminar de una columna dada ($2 en el ejemplo) los campos duplicados (separados por comas).
Archivo de entrada:
A 1,2,3,4
B 4,5,6,3
C 2,15
Salida esperada:
A 1,2,3,4
B 5,6
C 15
Respuesta aceptada:
perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e'
Puede ejecutar lo anterior así:
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e' afile
A 1,2,3,4
B 5,6
C 15
Cómo funciona
Primero llamando a perl
con -lpe
hace las siguientes 3 cosas.
-l[octal]
habilitar el procesamiento de final de línea, especifica el terminador de línea-p
asumir bucle como -n pero imprimir línea también, como sed-e program
una línea de programa (se permiten varias -e, omitir archivo de programa)
Básicamente, esto toma el archivo, elimina las líneas nuevas, opera en una línea y luego vuelve a agregar un carácter de línea nueva cuando termina. Por lo tanto, solo se trata de recorrer el archivo y ejecutar nuestro código Perl en cada uno de ellos.
En cuanto al código Perl real:
s
significa un carácter de espaciado (los cinco caracteres[ fnrt]
yv
en versiones más recientes deperl
, como[[:space:]]
).K
Mantenga las cosas que quedan de la K, no las incluya en $&S+
uno o más caracteres que no están en el conjunto [ fnrtv]
El join ",",
tomará los resultados y volverá a unir cada campo para que esté separado por una coma.
La split ",", $&
tomará las coincidencias encontradas por S+
y divídalos en solo los campos, sin la coma.
El grep {!$seen{$_}++}
tomará el número de cada campo, lo agregará al hash, $seen{}
donde el número de cada campo es $_
a medida que avanzamos por cada uno de ellos. Cada vez que se "ve" un número de campo, se cuenta a través del ++
operador, $seen{$_}++
.
El grep{!$seen{$_}++}
devolverá un valor de campo si solo se ha visto una vez.
Modificado para ver lo que está pasando
Si usa esta abominación modificada, puede ver lo que sucede a medida que este trazador de líneas de Perl se mueve a través de las líneas del archivo.
$ perl -lpe 's/sKS+/join ",", grep {!$seen{$_}++} split ",", $&/e; @a=keys %seen; @b=values %seen; print "keys: @a | vals: @b"' afile
keys: 4 1 3 2 | vals: 1 1 1 1
A 1,2,3,4
keys: 6 4 1 3 2 5 | vals: 1 2 1 2 1 1
B 5,6
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
C 15
Esto te muestra el contenido de $seen{}
al final del procesamiento de una línea del archivo. Tomemos la segunda línea del archivo.
B 4,5,6,3
Y así es como mi versión modificada muestra esa línea:
keys: 6 4 1 3 2 15 5 | vals: 1 2 1 2 2 1 1
Esto significa que hemos visto el campo n.° 6 (1 vez), el campo n.° 4 (2 veces), etc. y el campo n.° 5 (1 vez). Así que cuando grep{...}
devuelve los resultados, solo devolverá resultados de esta matriz si estaba presente en esta línea (4,5,6,3) y si la hemos visto solo 1 vez (6,1,15,5). La intersección de estas 2 listas es (5,6) y eso es lo que devuelve grep
.
Referencias
- perlre – perldoc.perl.org