unix shell scripting sed awk

¿Cómo puedo eliminar líneas duplicadas en un archivo en Unix?



shell scripting (7)

La primera solución también es de http://sed.sourceforge.net/sed1line.txt

$ echo -e ''1/n2/n2/n3/n3/n3/n4/n4/n4/n4/n5'' |sed -nr ''$!N;/^(.*)/n/1$/!P;D'' 1 2 3 4 5

la idea central es:

print ONLY once of each duplicate consecutive lines at its LAST appearance and use D command to implement LOOP.

Explica:

  1. $!N; : si la línea actual NO es la última, use el comando N para leer la siguiente línea en el pattern space .
  2. /^(.*)/n/1$/!P : si el contenido del pattern space actual es dos duplicate string separadas por /n , lo que significa que la siguiente línea es la same la línea actual, NO podemos imprimir de acuerdo con nuestra idea central de lo contrario, lo que significa que la línea actual es la ÚLTIMA aparición de todas sus líneas consecutivas duplicadas, ahora podemos usar el comando P para imprimir los caracteres en el uso pattern space actual /n ( /n también impreso).
  3. D : usamos el comando D para eliminar los caracteres en el uso pattern space actual /n ( /n también eliminado), luego el contenido del pattern space del pattern space es la siguiente línea.
  4. y el comando D obligará a sed a saltar a su FIRST comando $!N , pero NO leerá la siguiente línea desde el archivo o flujo de entrada estándar.

La segunda solución es fácil de entender (de mí mismo):

$ echo -e ''1/n2/n2/n3/n3/n3/n4/n4/n4/n4/n5'' |sed -nr ''p;:loop;$!N;s/^(.*)/n/1$//1/;tloop;D'' 1 2 3 4 5

la idea central es:

print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.

Explica:

  1. lea una nueva línea de flujo de entrada o archivo e imprímalo una vez.
  2. use :loop command establece una label llamada loop .
  3. usa N para leer la siguiente línea en el pattern space .
  4. use s/^(.*)/n/1$//1/ para eliminar la línea actual si la siguiente línea es la misma que la línea actual, usamos s comando s para realizar la acción de delete .
  5. si el comando s se ejecuta con éxito, use el comando tloop force sed para saltar a la label llamada loop , que hará el mismo loop a las siguientes líneas util no hay líneas consecutivas duplicadas de la latest printed ; de lo contrario, use el comando D para delete la línea que es la misma con la latest-printed line , y fuerce a sed para saltar al primer comando, que es el comando p , el contenido del pattern space actual es la siguiente línea nueva.

¿Hay alguna manera de eliminar líneas duplicadas en un archivo en Unix?

Puedo hacerlo con comandos sort -u y uniq , pero quiero usar sed o awk . ¿Es eso posible?


De http://sed.sourceforge.net/sed1line.txt : (Por favor, no me preguntes cómo funciona esto ;-))

# delete duplicate, consecutive lines from a file (emulates "uniq"). # First line in a set of duplicate lines is kept, rest are deleted. sed ''$!N; /^/(.*/)/n/1$/!P; D'' # delete duplicate, nonconsecutive lines from a file. Beware not to # overflow the buffer size of the hold space, or else use GNU sed. sed -n ''G; s//n/&&/; /^/([ -~]*/n/).*/n/1/d; s//n//; h; P''


El texto único que publicó Andre Miller funciona a excepción de las versiones recientes de sed cuando el archivo de entrada termina con una línea en blanco y sin caracteres. En mi Mac mi CPU simplemente gira.

Bucle infinito si la última línea está en blanco y no tiene caracteres :

sed ''$!N; /^/(.*/)/n/1$/!P; D''

No cuelga, pero pierdes la última línea

sed ''$d;N; /^/(.*/)/n/1$/!P; D''

La explicación está al final de las preguntas frecuentes sed :

El mantenedor de sed GNU sintió que a pesar de los problemas de portabilidad
esto causaría, cambiar el comando N para imprimir (en lugar de
eliminar) el espacio del patrón era más consistente con las propias intuiciones
acerca de cómo debe comportarse un comando para "agregar la siguiente línea".
Otro hecho que favoreció el cambio fue que "{N; command;}"
elimine la última línea si el archivo tiene un número impar de líneas, pero
imprime la última línea si el archivo tiene un número par de líneas.

Para convertir scripts que utilizaron el comportamiento anterior de N (eliminación
el espacio del patrón al llegar al EOF) a scripts compatibles con
todas las versiones de sed, cambian una "N" solitaria a "$ d; N" .


Perl one-liner similar a la solución awk de @ jonas:

perl -ne ''print if ! $x{$_}++'' file

Esta variación elimina los espacios en blanco finales antes de comparar:

perl -lne ''s//s*$//; print if ! $x{$_}++'' file

Esta variación edita el archivo in situ:

perl -i -ne ''print if ! $x{$_}++'' file

Esta variación edita el archivo in situ y file.bak una copia de seguridad file.bak

perl -i.bak -ne ''print if ! $x{$_}++'' file


Una forma alternativa de usar Vim (Vi compatible) :

Eliminar líneas duplicadas y consecutivas de un archivo:

vim -esu NONE +''g//v^(.*)/n/1$/d'' +wq

Elimine líneas duplicadas, no consecutivas y no vacías de un archivo:

vim -esu NONE +''g//v^(.+)$/_.{-}^/1$/d'' +wq


awk ''!seen[$0]++'' file.txt

seen es una matriz asociativa que Awk pasará a cada línea del archivo. Si una línea no está en la matriz, entonces seen[$0] se evaluará a falso. El ! es un operador NOT lógico e invertirá el falso en verdadero. Awk imprimirá las líneas donde la expresión se evalúa como verdadera. Los incrementos de ++ seen modo tal que se seen[$0] == 1 después de la primera vez que se encuentra una línea y luego se seen[$0] == 2 , y así sucesivamente.
Awk evalúa todo excepto 0 y "" (cadena vacía) en verdadero. Si se coloca una línea duplicada en seen entonces !seen[$0] se evaluará como falso y la línea no se escribirá en la salida.


cat filename | sort | uniq -c | awk -F" " ''$1<2 {print $2}''

Elimina las líneas duplicadas usando awk.