¿Existe una herramienta para obtener las líneas en un archivo que no están en otro?

Respuestas:

159

Si. La grepherramienta estándar para buscar archivos de cadenas de texto se puede utilizar para restar todas las líneas de un archivo a otro.

grep -F -x -v -f fileB fileA

Esto funciona utilizando cada línea en el archivo B como patrón ( -f fileB) y tratándola como una cadena simple para que coincida (no una expresión regular regular) ( -F). Obliga a que la coincidencia ocurra en toda la línea ( -x) e imprime solo las líneas que no coinciden ( -v). Por lo tanto, está imprimiendo las líneas en el archivo A que no contienen los mismos datos que cualquier línea en el archivo B.

La desventaja de esta solución es que no tiene en cuenta el orden de las líneas y si su entrada tiene líneas duplicadas en diferentes lugares, es posible que no obtenga lo que espera. La solución a eso es usar una herramienta de comparación real como diff. Puede hacer esto creando un archivo diff con el valor de contexto en el 100% de las líneas en el archivo, luego analizándolo solo para las líneas que se eliminarían si se convierte el archivo A al archivo B. (Tenga en cuenta que este comando también elimina el diff formateo después de obtener las líneas correctas).

diff -U $(wc -l < fileA) fileA fileB | sed -n 's/^-//p' > fileC
Caleb
fuente
@ inderpreet99 El -uargumento en minúscula realmente toma un parámetro de un número siempre que no sea seguido por un espacio. La ventaja de la forma en que lo tenía antes es que funcionará con o sin un valor, por lo que podría usar algo en esa rutina de subcomando que no devolvió salida. Mayúscula '-U' por otro lado requiere un argumento.
Caleb
tenga cuidado, grep -f es O (N ^ 2) Creo: stackoverflow.com/questions/4780203/…
rogerdpack
1
la difftubería funciona de maravilla gracias.
Felipe Alvarez el
Para tener en cuenta el problema de ordenación, puede usar la sustitución de proceso en el comando para procesar cada archivo antes de grepque sea necesario. Ejemplo:grep -F -x -v -f <(sort fileB) <(sort fileA)
Tony Cesaro el
@TonyCesaro Eso funcionaría si su conjunto de datos no es específico del pedido y no es necesario tener en cuenta los duplicados. La ventaja de usar diffes que la posición en el archivo se tiene en cuenta.
Caleb
57

La respuesta depende en gran medida del tipo y formato de los archivos que está comparando.

Si los archivos que está comparando son archivos de texto ordenados, entonces la herramienta GNU escrita por Richard Stallman y Davide McKenzie llamada commpuede realizar el filtrado que está buscando. Es parte de los coreutils.

Ejemplo

Digamos que tiene los siguientes 2 archivos:

$ cat a
1
2
3
4
5

$ cat b
1
2
3
4
5
6

Líneas en el archivo bque no están en el archivo a:

$ comm <(sort a) <(sort b) -3
    6
Un amigo
fuente
1
+1 por mencionar comm; desafortunadamente, commrequiere archivos ordenados
Arcege
11
entonces ordenarlos? comm <(ordenar a) <(ordenar b) -1 -2
Sirex
Esta es una sintaxis extraña. <()? Funciona y lo entiendo, pero ¿hay un nombre para esta rareza?
mlissner 05 de
2
@mlissner <()también se conoce como sustitución de procesos .
miku
1
commoriginalmente fue escrito alrededor de 1973 por alguien en Bell Labs, no rms. Te refieres a la implementación de GNU que vino mucho después. Ha habido muchas implementaciones diferentes de las utilidades de Unix a lo largo de los años.
Stéphane Chazelas
32

de stackoverflow ...

comm -23 archivo1 archivo2

-23 suprime las líneas que están en ambos archivos, o solo en el archivo 2. Los archivos tienen que ser ordenados (están en su ejemplo) pero si no, canalícelos primero por orden ...

Vea la página del manual aquí

JJS
fuente
Esto no funciona para mí, por alguna razón ...
Jan
@ ¿Están ordenados tus archivos? ¿Cómo los ordenaste?
JJS
8

Los métodos grep y comm (con clasificación) tardan mucho en archivos grandes. SiegeX y ghostdog74 compartieron dos excelentes métodos awk para extraer líneas exclusivas de uno de los dos archivos en Stack Overflow:

$ awk 'FNR==NR{a[$0]++}FNR!=NR && !a[$0]{print}' file1 file2

$ awk 'FNR==NR{a[$0]++;next}(!($0 in a))' file1 file2
Miles Wolbe
fuente
2
Si está haciendo esto con archivos enormes, entonces las restricciones de memoria de cargar un archivo enorme en una matriz asociativa serán prohibitivas.
Charles Duffy
4

Si los archivos son grandes y no tiene un orden personalizado para sus entradas, grep tarda demasiado. Una alternativa rápida sería

sort file1 > 1 
sort file2 > 2 
diff 1 2 | grep "\>" | sed -e 's/> //'

[file2-file1 resultados a la pantalla, canalización a archivo, etc.]

Cambiar >a <obtendría la resta opuesta.rm 1 2

Eshel Faraggi
fuente
2

También podría considerar vimdiff, destaca las diferencias entre archivos en un editor vim

simona
fuente
1
Pero, ¿hay una manera fácil de restar automáticamente en Vimdiff?
Kazark