¿Hay alguna manera de eliminar líneas duplicadas en un archivo en Unix?
Puedo hacerlo con sort -uy uniqcomandos, pero quiero usar sedo awk. ¿Es eso posible?
awk, pero consumirá muchos recursos en archivos más grandes.
¿Hay alguna manera de eliminar líneas duplicadas en un archivo en Unix?
Puedo hacerlo con sort -uy uniqcomandos, pero quiero usar sedo awk. ¿Es eso posible?
awk, pero consumirá muchos recursos en archivos más grandes.
Respuestas:
awk '!seen[$0]++' file.txt
seenes una matriz asociativa a la que Awk pasará cada línea del archivo. Si una línea no está en la matriz, seen[$0]se evaluará como falsa. El !es un operador lógico NOT e invertirá lo falso a verdadero. Awk imprimirá las líneas donde la expresión se evalúa como verdadera. Los ++incrementos seenpara que seen[$0] == 1después de la primera vez que se encuentre una línea y luego seen[$0] == 2, y así sucesivamente.
Awk evalúa todo menos 0y ""(cadena vacía) a verdadero. Si se coloca una línea duplicada, seenentonces !seen[$0]se evaluará como falsa y la línea no se escribirá en la salida.
awk '!seen[$0]++' merge_all.txt > output.txt
for f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
De http://sed.sourceforge.net/sed1line.txt : (Por favor, no me pregunten 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'
$!necesaria la parte? ¿No sed 'N; /^\(.*\)\n\1$/!P; D'hace lo mismo? No puedo encontrar un ejemplo en el que los dos sean diferentes en mi máquina (luego probé una línea vacía al final con ambas versiones y ambas estaban bien).
[ -~]representa un rango de caracteres ASCII de 0x20 (espacio) a 0x7E (tilde). Estos se consideran los caracteres ASCII imprimibles (la página vinculada también tiene 0x7F / eliminar, pero eso no parece correcto). Eso hace que la solución se rompa para cualquiera que no use ASCII o cualquiera que use, digamos, caracteres de tabulación ... El más portátil [^\n]incluye muchos más caracteres ... todos, excepto uno, de hecho.
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 en el lugar:
perl -i -ne 'print if ! $x{$_}++' file
Esta variación edita el archivo en el lugar y realiza una copia de seguridad file.bak
perl -i.bak -ne 'print if ! $x{$_}++' file
La línea que Andre Miller publicó anteriormente funciona excepto para 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 se cuelga, pero pierdes la última línea
sed '$d;N; /^\(.*\)\n\1$/!P; D'
La explicación se encuentra al final de las preguntas frecuentes de sed :
El mantenedor de sed de GNU consideró que, a pesar de los problemas de portabilidad que
esto causaría, cambiar el comando N para imprimir (en lugar de
eliminar) el espacio del patrón era más coherente con las intuiciones de uno
sobre cómo debería comportarse un comando para "agregar la siguiente línea" .
Otro hecho que favoreció el cambio fue que "{N; command;}"
eliminará la última línea si el archivo tiene un número impar de líneas, pero
imprimirá la última línea si el archivo tiene un número par de líneas.Para convertir los scripts que usaban el comportamiento anterior de N (eliminar
el espacio del patrón al llegar al EOF) a scripts compatibles con
todas las versiones de sed, cambie una "N" solitaria. a "$ d; N;" .
$ 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:
$!N;: si la línea actual NO es la última línea, use el Ncomando para leer la siguiente línea pattern space./^(.*)\n\1$/!P: si el contenido de la corriente pattern spaceestá duplicate stringseparado por dos \n, lo que significa que la siguiente línea es samecon la línea actual, NO podemos imprimirlo 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 Pcomando para imprimir los caracteres en la pattern spaceutilidad actual \n( \ntambién impresa).D: utilizamos el Dcomando para eliminar los caracteres en la pattern spaceutilidad actual \n( \ntambién eliminada), luego el contenido depattern space es la siguiente línea.Dcomando obligará seda saltar a su FIRSTcomando $!N, pero NO leerá la siguiente línea del archivo o flujo de entrada estándar.$ 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:
:loopcomando set a labelnamed loop.Npara leer la siguiente línea en el pattern space.s/^(.*)\n\1$/\1/para eliminar la línea actual si la siguiente línea es la misma que la línea actual, usamos el scomando para realizar la deleteacción.scomando se ejecuta con éxito, utilice la tloopfuerza del comando sedpara saltar al labelnombre loop, que hará el mismo bucle a las siguientes líneas, no hay líneas consecutivas duplicadas de la línea que es latest printed; de lo contrario, use el Dcomando para deletela línea que es la misma que la latest-printed line, y fuerce sedpara saltar al primer comando, que es el pcomando, el contenido de current pattern spacees la siguiente línea nueva.busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
cat filename | sort | uniq -c | awk -F" " '$1<2 {print $2}'
Elimina las líneas duplicadas usando awk.
cates inútil. De todos modos, uniqya lo hace por sí mismo y no requiere que la entrada sea exactamente una palabra por línea.
uniqsolo es suficiente.