regex bash unix text editor

sed regex



Eliminar duplicados del archivo de texto basado en el segundo archivo de texto (4)

¿Cómo puedo eliminar todas las líneas de un archivo de texto ( main.txt ) marcando un segundo archivo de texto ( main.txt )? ¿Cuál es un enfoque eficiente si los archivos son mayores de 10-100mb? [Usando mac]

Ejemplo:

main.txt 3 1 2 5

Eliminar estas líneas

removethese.txt 3 2 9

Salida:

output.txt 1 5

Líneas de ejemplo (estas son las líneas reales con las que estoy trabajando; el orden no importa):

ChIJW3p7Xz8YyIkRBD_TjKGJRS0 ChIJ08x-0kMayIkR5CcrF-xT6ZA ChIJIxbjOykFyIkRzugZZ6tio1U ChIJiaF4aOoEyIkR2c9WYapWDxM ChIJ39HoPKDix4kRcfdIrxIVrqs ChIJk5nEV8cHyIkRIhmxieR5ak8 ChIJs9INbrcfyIkRf0zLkA1NJEg ChIJRycysg0cyIkRArqaCTwZ-E8 ChIJC8haxlUDyIkRfSfJOqwe698 ChIJxRVp80zpcEARAVmzvlCwA24 ChIJw8_LAaEEyIkR68nb8cpalSU ChIJs35yqObit4kR05F4CXSHd_8 ChIJoRmgSdwGyIkRvLbhOE7xAHQ ChIJaTtWBAWyVogRcpPDYK42-Nc ChIJTUjGAqunVogR90Kc8hriW8c ChIJN7P2NF8eVIgRwXdZeCjL5EQ ChIJizGc0lsbVIgRDlIs85M5dBs ChIJc8h6ZqccVIgR7u5aefJxjjc ChIJ6YMOvOeYVogRjjCMCL6oQco ChIJ54HcCsaeVogRIy9___RGZ6o ChIJif92qn2YVogR87n0-9R5tLA ChIJ0T5e1YaYVogRifrl7S_oeM8 ChIJwWGce4eYVogRcrfC5pvzNd4


Aquí hay muchas de las soluciones simples y efectivas que he encontrado: http://www.catonmat.net/blog/set-operations-in-unix-shell-simplified/

Necesita usar uno de los comandos bash de Set Complement . Los archivos de 100 MB se pueden resolver en segundos o minutos.

Establecer membresía

$ grep -xc ''element'' set # outputs 1 if element is in set # outputs >1 if set is a multi-set # outputs 0 if element is not in set $ grep -xq ''element'' set # returns 0 (true) if element is in set # returns 1 (false) if element is not in set $ awk ''$0 == "element" { s=1; exit } END { exit !s }'' set # returns 0 if element is in set, 1 otherwise. $ awk -v e=''element'' ''$0 == e { s=1; exit } END { exit !s }''

Establecer igualdad

$ diff -q <(sort set1) <(sort set2) # returns 0 if set1 is equal to set2 # returns 1 if set1 != set2 $ diff -q <(sort set1 | uniq) <(sort set2 | uniq) # collapses multi-sets into sets and does the same as previous $ awk ''{ if (!($0 in a)) c++; a[$0] } END{ exit !(c==NR/2) }'' set1 set2 # returns 0 if set1 == set2 # returns 1 if set1 != set2 $ awk ''{ a[$0] } END{ exit !(length(a)==NR/2) }'' set1 set2 # same as previous, requires >= gnu awk 3.1.5

Establecer cardinalidad

$ wc -l set | cut -d'' '' -f1 # outputs number of elements in set $ wc -l < set $ awk ''END { print NR }'' set

Prueba de subconjunto

$ comm -23 <(sort subset | uniq) <(sort set | uniq) | head -1 # outputs something if subset is not a subset of set # does not putput anything if subset is a subset of set $ awk ''NR==FNR { a[$0]; next } { if !($0 in a) exit 1 }'' set subset # returns 0 if subset is a subset of set # returns 1 if subset is not a subset of set

Establecer unión

$ cat set1 set2 # outputs union of set1 and set2 # assumes they are disjoint $ awk 1 set1 set2 # ditto $ cat set1 set2 ... setn # union over n sets $ cat set1 set2 | sort -u # same, but assumes they are not disjoint $ sort set1 set2 | uniq # sort -u set1 set2 $ awk ''!a[$0]++'' # ditto

Establecer intersección

$ comm -12 <(sort set1) <(sort set2) # outputs insersect of set1 and set2 $ grep -xF -f set1 set2 $ sort set1 set2 | uniq -d $ join <(sort -n A) <(sort -n B) $ awk ''NR==FNR { a[$0]; next } $0 in a'' set1 set2

Establecer complemento

$ comm -23 <(sort set1) <(sort set2) # outputs elements in set1 that are not in set2 $ grep -vxF -f set2 set1 # ditto $ sort set2 set2 set1 | uniq -u # ditto $ awk ''NR==FNR { a[$0]; next } !($0 in a)'' set2 set1

Establecer diferencia simétrica

$ comm -3 <(sort set1) <(sort set2) | sed ''s//t//g'' # outputs elements that are in set1 or in set2 but not both $ comm -3 <(sort set1) <(sort set2) | tr -d ''/t'' $ sort set1 set2 | uniq -u $ cat <(grep -vxF -f set1 set2) <(grep -vxF -f set2 set1) $ grep -vxF -f set1 set2; grep -vxF -f set2 set1 $ awk ''NR==FNR { a[$0]; next } $0 in a { delete a[$0]; next } 1; END { for (b in a) print b }'' set1 set2

Set de poder

$ p() { [ $# -eq 0 ] && echo || (shift; p "$@") | while read r ; do echo -e "$1 $r/n$r"; done } $ p `cat set` # no nice awk solution, you are welcome to email me one: # [email protected]

Establecer producto cartesiano

$ while read a; do while read b; do echo "$a, $b"; done < set1; done < set2 $ awk ''NR==FNR { a[$0]; next } { for (i in a) print i, $0 }'' set1 set2

Prueba de conjunto disjunto

$ comm -12 <(sort set1) <(sort set2) # does not output anything if disjoint $ awk ''++seen[$0] == 2 { exit 1 }'' set1 set2 # returns 0 if disjoint # returns 1 if not

Prueba de conjunto vacío

$ wc -l < set # outputs 0 if the set is empty # outputs >0 if the set is not empty $ awk ''{ exit 1 }'' set # returns 0 if set is empty, 1 otherwise

Mínimo

$ head -1 <(sort set) # outputs the minimum element in the set $ awk ''NR == 1 { min = $0 } $0 < min { min = $0 } END { print min }''

Máximo

$ tail -1 <(sort set) # outputs the maximum element in the set $ awk ''$0 > max { max = $0 } END { print max }''


Hay dos formas estándar de hacer esto:

Con grep :

grep -vxFf removethese main

Esto usa:

  • -v para invertir el partido.
  • -x coincide con la línea completa, para evitar, por ejemplo, he coincida con líneas como hello o highway to hell .
  • -F para usar cadenas fijas, de modo que el parámetro se tome como está, no se interprete como una expresión regular.
  • -f para obtener los patrones de otro archivo. En este caso, de removethese .

Con awk :

$ awk ''FNR==NR {a[$0];next} !($0 in a)'' removethese main 1 5

De esta manera, almacenamos cada línea en removethese en una matriz a[] . Luego, leemos el archivo main e imprimimos las líneas que no están presentes en la matriz.


Me gusta el uso de awk de @ fedorqui para configuraciones donde uno tiene suficiente memoria para todas las líneas de "eliminar estas": una expresión concisa de un enfoque en memoria.

Pero para un escenario donde el tamaño de las líneas para eliminar es grande en relación con la memoria actual, y leer esos datos en una estructura de datos en memoria es una invitación a fallar o agitarse, considere un enfoque antiguo: ordenar / unir

sort main.txt > main_sorted.txt sort removethese.txt > removethese_sorted.txt join -t '''' -v 1 main_sorted.txt removethese_sorted.txt > output.txt

Notas:

  • esto no conserva el orden de main.txt: las líneas en output.txt se ordenarán
  • requiere suficiente disco para que la ordenación haga lo suyo (archivos temporales) y almacene versiones ordenadas del mismo tamaño de los archivos de entrada
  • tener la opción -v de join hacer justo lo que queremos aquí: imprimir "no deseable" desde el archivo 1, soltar coincidencias, es un poco de casualidad
  • no trata directamente las configuraciones regionales, la clasificación, las claves, etc. - se basa en los valores predeterminados de clasificación y unión (-t con un argumento vacío) para que coincida con el orden de clasificación, que funciona en mi máquina actual

Con grep :

grep -vxFf removethese.txt main.txt >output.txt

Con fgrep :

fgrep -vxf removethese.txt main.txt >output.txt

fgrep está en desuso. fgrep --help dice:

La invocación como ''fgrep'' está en desuso; use ''grep -F'' en su lugar.

Con awk (de @fedorqui):

awk ''FNR==NR {a[$0];next} !($0 in a)'' removethese.txt main.txt >output.txt

Con sed :

sed "s=^=/^=;s=$=$/d=" removethese.txt | sed -f- main.txt >output.txt

Esto fallará si removethese.txt contiene caracteres especiales. Para eso puedes hacer:

sed ''s/[^^]/[&]/g; s//^///^/g'' removethese.txt >newremovethese.txt

y use este newremovethese.txt en el comando sed . Pero no vale la pena el esfuerzo, es demasiado lento en comparación con los otros métodos.

Prueba realizada en los métodos anteriores:

El método sed lleva demasiado tiempo y no vale la pena probarlo.

Archivos utilizados:

removethese.txt : Size: 15191908 (15MB) Blocks: 29672 Lines: 100233 main.txt : Size: 27640864 (27.6MB) Blocks: 53992 Lines: 180034

Comandos:
grep -vxFf | fgrep -vxf | awk

Tiempo tomado:
0m7.966s | 0m7.823s | 0m0.237s
0m7.877s | 0m7.889s | 0m0.241s
0m7.971s | 0m7.844s | 0m0.234s
0m7.864s | 0m7.840s | 0m0.251s
0m7.798s | 0m7.672s | 0m0.238s
0m7.793s | 0m8.013s | 0m0.241s

AVG
0m7.8782s | 0m7.8468s | 0m0.2403s

El resultado de esta prueba implica que fgrep es un poco más rápido que grep .

El método awk (de @fedorqui) pasa la prueba con gran 0.2403 seconds (¡solo 0.2403 seconds !).

Entorno de prueba:

HP ProBook 440 G1 Laptop 8GB RAM 2.5GHz processor with turbo boost upto 3.1GHz RAM being used: 2.1GB Swap being used: 588MB RAM being used when the grep/fgrep command is run: 3.5GB RAM being used when the awk command is run: 2.2GB or less Swap being used when the commands are run: 588MB (No change)

Resultado de la prueba:

Usa el método awk .