Diferencia de dos archivos grandes

14

Tengo "test1.csv" y contiene

200,400,600,800
100,300,500,700
50,25,125,310

y test2.csv y contiene

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

ahora

diff test2.csv test1.csv > result.csv

es diferente a

diff test1.csv test2.csv > result.csv

No sé cuál es el orden correcto, pero quiero otra cosa, los dos comandos anteriores generarán algo como

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Quiero mostrar solo la diferencia, por lo tanto, results.csv debería verse así

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Lo intenté diff -qy diff -sno hicieron el truco. El orden no importa, lo que importa es que solo quiero ver la diferencia, no> ni <ni espacio en blanco.

grep -FvF hizo el truco en archivos más pequeños no en archivos grandes

El primer archivo contiene más de 5 millones de líneas, el segundo archivo contiene 1300.

entonces results.csv debería dar como resultado ~ 4,998,700 líneas

También probé grep -F -x -v -f lo que no funcionó.

Lynob
fuente
1
@Tim vi tu enlace y soy un miembro antiguo, así que conozco las reglas, pero fue descuidado, lo siento :) lo estaba editando, y vi una ventana emergente que decía que la publicación fue editada, así que hiciste el trabajo por mí y estoy Agradecido señor.
Lynob
50,25,125,310es común tanto a la necesidad de eliminar file..you que desde su salida deseada ..
heemayl
¿Se debe preservar el orden?
kos
1
depende de lo que quieras hacer con la información, diff, IMO, es para hacer un parche. En cualquier caso, ahora estoy seguro de su mejor herramienta, diff, grep, awk o perl.
Panther

Respuestas:

20

Suena como un trabajo para comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Como se explica en man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Por lo tanto, -3significa que solo se imprimirán las líneas que son exclusivas de uno de los archivos. Sin embargo, estos están sangrados según el archivo en el que se encontraron. Para eliminar la pestaña, use:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

En este caso, ni siquiera necesita ordenar los archivos y puede simplificar lo anterior para:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv
terdon
fuente
No te han engañado los espacios después de la 200,[...]línea, ¿eh? :)
kos
@kos no, eliminé los espacios finales de los archivos primero. Asumí que los archivos del OP no los tienen.
terdon
6

Utilizando grepcon la bashsustitución de procesos:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Para guardar la salida como results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()es el patrón de sustitución del bashproceso

  • grep -vFf test2.csv test1.csv encontrará las líneas exclusivas de solo test1.csv

  • grep -vFf test1.csv test2.csv encontrará las líneas exclusivas de solo test2.csv

  • Finalmente estamos resumiendo los resultados por cat

O como sugirió Oli , puede usar la agrupación de comandos también:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

O simplemente ejecute uno tras otro, ya que ambos escriben en STDOUT, finalmente se agregarán:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5
heemayl
fuente
1
¿Por qué catdos comandos redirigidos? ¿Por qué no simplemente ejecutar uno y luego el otro? grep ... ; grep ...o { grep ... ; grep ... ; }si quisieras hacer algo con la salida colectiva.
Oli
@Oli Gracias ... es una gran idea ... No pensé en eso ...
heemayl
4

Si el orden de las filas no es relevante, use awko perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

Use greppara obtener las líneas comunes y filtrarlas:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

El grep interno obtiene las líneas comunes, luego el grep externo encuentra líneas que no coinciden con estas líneas comunes.

muru
fuente
Su comando awk simplemente se vuelve a implementar sort | uniq -u, lo que da una respuesta incorrecta cuando un archivo contiene líneas duplicadas. Para grep, yo diría "interno" / "externo", no "interno" / "externo".
Peter Cordes
@PeterCordes sí, lo hace y ¿quién eres tú para decir que ese es el resultado incorrecto?
muru
Incorrecto en el sentido de que no es exactamente lo que pidió la pregunta, en ese caso de esquina. Podría ser lo que alguien quiere, pero se debe señalar la diferencia entre lo que su awkva a imprimir y lo que el comm -3y diffrespuestas se imprimirá.
Peter Cordes
@PeterCordes nuevamente, ¿quién eres para decir eso? Hasta que el OP diga que es lo que quieren, no me importa si la salida difiere de la de comm -3. No veo ninguna razón por la que deba explicar eso. Si desea editar en una nota, siéntase libre.
muru
El OP dijo que quiere la diferencia. Eso no siempre es lo que produce su programa. Un programa que produce la misma salida para un caso de prueba, pero no satisface la descripción como está escrita para todos los casos, requiere un aviso. Estoy aquí para decir eso, y es cierto independientemente de quién soy o quién eres. Agregué una nota.
Peter Cordes
4

Usa las --*-line-format=...opciones dediff

Puede decir diffexactamente lo que necesita, explicado a continuación:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

Es posible especificar la salida de diff de una manera muy detallada, similar a un printfformato de número.

Las líneas del primer archivo test1.csvse denominan líneas "antiguas", y las líneas del segundo test2.csv, son líneas "nuevas". Eso tiene sentido cuando diffse usa para ver qué cambió en un archivo.

Las opciones que necesitamos son las que establecen el formato para las líneas "antiguas", las líneas "nuevas" y las líneas "sin cambios".
Los formatos que necesitamos son muy simples:
para las líneas cambiadas, nuevas y antiguas, queremos generar solo el texto de las líneas. %Les el símbolo de formato para el texto de línea.
Para las líneas sin cambios, no queremos mostrar nada.

Con esto, podemos escribir opciones como --old-line-format='%L', y ponerlo todo junto, usando sus datos de ejemplo:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Notas sobre el rendimiento

Debido a que los archivos tienen un tamaño diferente, intente intercambiar los archivos de entrada si no importa, podría ser que el funcionamiento interno de se diffpueda manejar de una manera mejor que la otra. Mejor es necesitar menos memoria o menos cómputo.

Hay una opción de optimización para el uso diffcon archivos de gran tamaño: --speed-large-files. Utiliza suposiciones sobre la estructura del archivo, por lo que no está claro si ayuda en su caso, pero vale la pena intentarlo.

Las opciones de formato se describen en el man diffbajo --LTYPE-line-format=LFMT.

Volker Siegel
fuente
3

Dado que el pedido no necesita ser preservado, simplemente:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: se fusiona y clasifica test1.csvytest2.csv
  • uniq -u: imprime solo las líneas que no tienen duplicado
kos
fuente
Eso no funciona si un archivo contiene una línea dos veces, eso no aparece en el otro archivo. Ambas ocurrencias estarían en un diffresultado.
Volker Siegel