Comparar dos archivos en la terminal de Linux


168

Hay dos archivos llamados "a.txt" y "b.txt", ambos tienen una lista de palabras. Ahora quiero comprobar qué palabras son adicionales en "a.txt" y no en "b.txt" .

Necesito un algoritmo eficiente ya que necesito comparar dos diccionarios.


27
diff a.txt b.txt¿no es suficiente?
ThanksForAllTheFish

¿Pueden aparecer las palabras varias veces en cada archivo? ¿Puedes ordenar los archivos?
Basile Starynkevitch

solo necesito esas palabras que no están presentes en "b.txt" y están presentes en a.txt
Ali Imran

Respuestas:


343

si tienes vim instalado, prueba esto:

vimdiff file1 file2

o

vim -d file1 file2

Lo encontrarás fantástico.ingrese la descripción de la imagen aquí


9
definitivamente impresionante, bueno en diseño y fácil de encontrar las diferencias. Ohmygod
Zen

1
Su respuesta es asombrosa, pero mi maestra me pidió que no usara ninguna función de biblioteca: P
Ali Imran

1
¡Qué gran herramienta! Esto es inmensamente útil.
user1205577

1
¿Cuáles son los significados de esos colores?
zygimantus

1
Los códigos coloreados significan que son diferentes en dos archivos. @zygimantus
Fengya Li

73

Clasifícalos y usa comm:

comm -23 <(sort a.txt) <(sort b.txt)

commcompara archivos de entrada (ordenados) y, de forma predeterminada, genera tres columnas: líneas que son exclusivas de a, líneas que son exclusivas de b y líneas que están presentes en ambas. Al especificar -1, -2y / o -3puede suprimir la salida correspondiente. Por lo tanto comm -23 a b, solo enumera las entradas que son exclusivas de a. Utilizo la <(...)sintaxis para ordenar los archivos sobre la marcha, si ya están ordenados, no necesita esto.


He agregado mi propia respuesta usando solo comandos grep, por favor dime ¿es más eficiente?
Ali Imran

3
@AliImran, commes más eficiente porque hace el trabajo en una sola ejecución, sin almacenar todo el archivo en la memoria. Como está utilizando diccionarios que probablemente ya están ordenados, ni siquiera los necesita sort. El uso, grep -f file1 file2por otro lado, cargará todo file1en la memoria y comparará cada línea file2con todas esas entradas, lo cual es mucho menos eficiente. Es sobre todo útil para pequeños, sin clasificar -f file1.
Anders Johansson

1
Gracias @AndersJohansson por compartir el comando "comm". Es ingenioso de hecho. Con frecuencia tengo que hacer uniones externas entre archivos y esto funciona.
blispr

Presta atención al nuevo carácter de línea ... Acabo de descubrir que \ntambién se incluirá para hacer la comparación.
Bin


28

Puede usar la diffherramienta en Linux para comparar dos archivos. Puede usar las opciones --changed-group-format y --unchanged-group-format-format para filtrar los datos requeridos.

Las siguientes tres opciones pueden usarse para seleccionar el grupo relevante para cada opción:

  • '% <' obtiene líneas de FILE1

  • '%>' obtiene líneas de FILE2

  • '' (cadena vacía) para eliminar líneas de ambos archivos.

Por ejemplo: diff --changed-group-format = "% <" --unchanged-group-format = "" file1.txt file2.txt

[root@vmoracle11 tmp]# cat file1.txt 
test one
test two
test three
test four
test eight
[root@vmoracle11 tmp]# cat file2.txt 
test one
test three
test nine
[root@vmoracle11 tmp]# diff --changed-group-format='%<' --unchanged-group-format='' file1.txt file2.txt 
test two
test four
test eight

27

Si prefiere el estilo de salida diff git diff, puede usarlo con el --no-indexindicador para comparar archivos que no están en un repositorio git:

git diff --no-index a.txt b.txt

Utilizando un par de archivos con alrededor de 200k cadenas de nombre de archivo en cada uno, comparé (con el timecomando incorporado ) este enfoque frente a algunas de las otras respuestas aquí:

git diff --no-index a.txt b.txt
# ~1.2s

comm -23 <(sort a.txt) <(sort b.txt)
# ~0.2s

diff a.txt b.txt
# ~2.6s

sdiff a.txt b.txt
# ~2.7s

vimdiff a.txt b.txt
# ~3.2s

commparece ser el más rápido con diferencia, mientras que git diff --no-indexparece ser el enfoque más rápido para la salida de estilo diff.


Actualización 2018-03-25 En realidad, puede omitir el --no-indexindicador a menos que esté dentro de un repositorio git y desee comparar archivos no rastreados dentro de ese repositorio. De las páginas del manual :

Este formulario es para comparar las dos rutas dadas en el sistema de archivos. Puede omitir la opción --no-index cuando ejecuta el comando en un árbol de trabajo controlado por Git y al menos uno de los puntos de ruta fuera del árbol de trabajo, o cuando ejecuta el comando fuera de un árbol de trabajo controlado por Git.




4

Uso comm -13 (requiere archivos ordenados) :

$ cat file1
one
two
three

$ cat file2
one
two
three
four

$ comm -13 <(sort file1) <(sort file2)
four

1

Aquí está mi solución para esto:

mkdir temp
mkdir results
cp /usr/share/dict/american-english ~/temp/american-english-dictionary
cp /usr/share/dict/british-english ~/temp/british-english-dictionary
cat ~/temp/american-english-dictionary | wc -l > ~/results/count-american-english-dictionary
cat ~/temp/british-english-dictionary | wc -l > ~/results/count-british-english-dictionary
grep -Fxf ~/temp/american-english-dictionary ~/temp/british-english-dictionary > ~/results/common-english
grep -Fxvf ~/results/common-english ~/temp/american-english-dictionary > ~/results/unique-american-english
grep -Fxvf ~/results/common-english ~/temp/british-english-dictionary > ~/results/unique-british-english

2
¿Has probado alguna de las otras soluciones? ¿Alguna de estas soluciones te fue útil? Su pregunta es lo suficientemente genérica como para atraer a muchos usuarios, pero su respuesta es más específica para mi gusto ... Para mi caso particular sdiff -s file1 file2fue útil.
Metafaniel

@Metafaniel mi solución no utiliza el comando sdiff. Solo utiliza los comandos integrados de Linux para resolver el problema.
Ali Imran

-1

Usando awk para ello. Archivos de prueba:

$ cat a.txt
one
two
three
four
four
$ cat b.txt
three
two
one

El awk:

$ awk '
NR==FNR {                    # process b.txt  or the first file
    seen[$0]                 # hash words to hash seen
    next                     # next word in b.txt
}                            # process a.txt  or all files after the first
!($0 in seen)' b.txt a.txt   # if word is not hashed to seen, output it

Se duplican los duplicados:

four
four

Para evitar duplicados, agregue cada palabra recién encontrada en a.txt a seenhash:

$ awk '
NR==FNR {
    seen[$0]
    next
}
!($0 in seen) {              # if word is not hashed to seen
    seen[$0]                 # hash unseen a.txt words to seen to avoid duplicates 
    print                    # and output it
}' b.txt a.txt

Salida:

four

Si las listas de palabras están separadas por comas, como:

$ cat a.txt
four,four,three,three,two,one
five,six
$ cat b.txt
one,two,three

tienes que hacer un par de vueltas adicionales ( forbucles):

awk -F, '                    # comma-separated input
NR==FNR {
    for(i=1;i<=NF;i++)       # loop all comma-separated fields
        seen[$i]
    next
}
{
    for(i=1;i<=NF;i++)
        if(!($i in seen)) {
             seen[$i]        # this time we buffer output (below):
             buffer=buffer (buffer==""?"":",") $i
        }
    if(buffer!="") {         # output unempty buffers after each record in a.txt
        print buffer
        buffer=""
    }
}' b.txt a.txt

Salida esta vez:

four
five,six
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.