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.
Para guardarlo en un archivo podemos hacer estoawk '!seen[$0]++' merge_all.txt > output.txt
Akash Kandpal
55
Una advertencia importante aquí: si necesita hacer esto para varios archivos, y agrega más archivos al final del comando, o usa un comodín ... la matriz 'visto' se llenará con líneas duplicadas de TODOS los archivos. Si, en cambio, desea tratar cada archivo de forma independiente, deberá hacer algo comofor f in *.txt; do gawk -i inplace '!seen[$0]++' "$f"; done
Nick K9
@ NickK9 que eliminar el duplicado de forma acumulativa en varios archivos es impresionante en sí mismo. Buen consejo
# 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'
geekery ;-) +1, pero el consumo de recursos es inevitable.
Michael Krelin - hacker
3
'$! N; /^(.*)\n\1$/!P; D 'significa "Si no está en la última línea, lea en otra línea. Ahora mire lo que tiene y si NO es algo seguido de una nueva línea y luego lo mismo nuevamente, imprima el material. Ahora elimine las cosas (hasta la nueva línea) ".
Beta
2
'GRAMO; s / \ n / && /; / ^ ([- ~] * \ n). * \ n \ 1 / d; s / \ n //; h; P 'significa, más o menos, "Añade todo el espacio de espera a esta línea, luego, si ves una línea duplicada, tira todo, de lo contrario, copia todo el desorden en el espacio de espera e imprime la primera parte (que es la línea que acabas de leer "
Beta
¿Es $!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).
eddi
1
Casi 7 años después y nadie respondió @amichair ... <sniff> me pone triste. ;) De todos modos, [ -~]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.
Capa B
14
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
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 :
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;" .
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.
y el Dcomando obligará seda saltar a su FIRSTcomando $!N, pero NO leerá la siguiente línea del archivo o flujo de entrada estándar.
La segunda solución es fácil de entender (de mí mismo):
print ONLY once of each duplicate consecutive lines at its FIRST appearance and use : command & t command to implement LOOP.
Explica:
lea una nueva línea del flujo o archivo de entrada e imprímala una vez.
use el :loopcomando set a labelnamed loop.
use Npara leer la siguiente línea en el pattern space.
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 el scomando para realizar la deleteacción.
si el 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.
uniq
solo es suficiente.awk
, pero consumirá muchos recursos en archivos más grandes.Respuestas:
seen
es 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++
incrementosseen
para queseen[$0] == 1
después de la primera vez que se encuentre una línea y luegoseen[$0] == 2
, y así sucesivamente.Awk evalúa todo menos
0
y""
(cadena vacía) a verdadero. Si se coloca una línea duplicada,seen
entonces!seen[$0]
se evaluará como falsa y la línea no se escribirá en la salida.fuente
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 ;-))
fuente
$!
necesaria la parte? ¿Nosed '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:
Esta variación elimina los espacios en blanco finales antes de comparar:
Esta variación edita el archivo en el lugar:
Esta variación edita el archivo en el lugar y realiza una copia de seguridad
file.bak
fuente
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 :
fuente
Una forma alternativa de usar Vim (compatible con Vi) :
Eliminar duplicados, líneas consecutivas de un archivo:
vim -esu NONE +'g/\v^(.*)\n\1$/d' +wq
Eliminar líneas duplicadas, no consecutivas y no vacías de un archivo:
vim -esu NONE +'g/\v^(.+)$\_.{-}^\1$/d' +wq
fuente
La primera solución también es de http://sed.sourceforge.net/sed1line.txt
La idea central es:
Explica:
$!N;
: si la línea actual NO es la última línea, use elN
comando para leer la siguiente líneapattern space
./^(.*)\n\1$/!P
: si el contenido de la corrientepattern space
estáduplicate string
separado por dos\n
, lo que significa que la siguiente línea essame
con 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 elP
comando para imprimir los caracteres en lapattern space
utilidad actual\n
(\n
también impresa).D
: utilizamos elD
comando para eliminar los caracteres en lapattern space
utilidad actual\n
(\n
también eliminada), luego el contenido depattern space
es la siguiente línea.D
comando obligarásed
a saltar a suFIRST
comando$!N
, pero NO leerá la siguiente línea del archivo o flujo de entrada estándar.La segunda solución es fácil de entender (de mí mismo):
La idea central es:
Explica:
:loop
comando set alabel
namedloop
.N
para leer la siguiente línea en elpattern 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 els
comando para realizar ladelete
acción.s
comando se ejecuta con éxito, utilice latloop
fuerza del comandosed
para saltar allabel
nombreloop
, que hará el mismo bucle a las siguientes líneas, no hay líneas consecutivas duplicadas de la línea que eslatest printed
; de lo contrario, use elD
comando paradelete
la línea que es la misma que lalatest-printed line
, y fuercesed
para saltar al primer comando, que es elp
comando, el contenido de currentpattern space
es la siguiente línea nueva.fuente
busybox echo -e "1\n2\n2\n3\n3\n3\n4\n4\n4\n4\n5" | busybox sed -nr "$!N;/^(.*)\n\1$/!P;D"
Esto se puede lograr usando awk
Abajo de la línea se mostrarán valores únicos
Puede generar estos valores únicos en un nuevo archivo
nuevo archivo uniq_file_name contendrá solo valores únicos, no duplicados
fuente
Elimina las líneas duplicadas usando awk.
fuente
cat
es inútil. De todos modos,uniq
ya lo hace por sí mismo y no requiere que la entrada sea exactamente una palabra por línea.