Comando Unix para encontrar líneas comunes en dos archivos

Estoy seguro de que una vez encontré un comando de Unix que podría imprimir las líneas comunes de dos o más archivos, ¿alguien sabe su nombre? Fue mucho más simple que diff .

El comando que buscas es comm . p.ej:-

 comm -12 1.sorted.txt 2.sorted.txt 

Aquí:

-1 : suprima la columna 1 (líneas exclusivas de 1.sorted.txt)

-2 : suprima la columna 2 (líneas exclusivas de 2.sorted.txt)

Para aplicar fácilmente el comando comm a archivos no ordenados , use la sustitución de proceso de Bash:

 $ bash --version GNU bash, version 3.2.51(1)-release Copyright (C) 2007 Free Software Foundation, Inc. $ cat > abc 123 567 132 $ cat > def 132 777 321 

Entonces los archivos abc y def tienen una línea en común, la que tiene “132”. Uso de comm en archivos sin clasificar:

 $ comm abc def 123 132 567 132 777 321 $ comm -12 abc def # No output! The common line is not found $ 

La última línea no produjo salida, la línea común no fue descubierta.

Ahora use comm en los archivos ordenados, clasificando los archivos con la sustitución del proceso:

 $ comm < ( sort abc ) <( sort def ) 123 132 321 567 777 $ comm -12 <( sort abc ) <( sort def ) 132 

¡Ahora tenemos la línea 132!

Tal vez te refieres a comm ?

Compare los archivos clasificados FILE1 y FILE2 línea por línea.

Sin opciones, produce una salida de tres columnas. La columna uno contiene líneas exclusivas de FILE1, la columna dos contiene líneas exclusivas de FILE2, y la columna tres contiene líneas comunes a ambos archivos.

El secreto para encontrar esta información son las páginas de información. Para los progtwigs de GNU, son mucho más detallados que sus páginas de manual. Prueba info coreutils y te mostrará todas las pequeñas utilidades útiles.

Para complementar el one-liner de Perl, aquí está su equivalente awk :

 awk 'NR==FNR{arr[$0];next} $0 in arr' file1 file2 

Esto leerá todas las líneas del file1 en el arreglo arr[] , y luego buscará cada línea en el file2 si ya existe dentro de la matriz (es decir, file1 ). Las líneas que se encuentran se imprimirán en el orden en que aparecen en el file2 . Tenga en cuenta que la comparación in arr utiliza toda la línea del file2 como índice de la matriz, por lo que solo informará las coincidencias exactas en líneas enteras.

Mientras

 grep -v -f 1.txt 2.txt > 3.txt 

le da las diferencias de dos archivos (lo que está en 2.txt y no en 1.txt), podría hacer fácilmente un

 grep -f 1.txt 2.txt > 3.txt 

para recostackr todas las líneas comunes, lo que debería proporcionar una solución fácil a su problema. Si ha ordenado archivos, debe tomar comm no obstante. ¡Saludos!

 perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' file1 file2 
 awk 'NR==FNR{a[$1]++;next} a[$1] ' file1 file2 

En una versión limitada de Linux (como un QNAP (nas) en el que estaba trabajando):
– el comunicador no existía
grep -f file1 file2 puede causar algunos problemas como dice @ChristopherSchultz y usar grep -F -f file1 file2 fue realmente lento (más de 5 minutos – no terminado) durante más de 2-3 segundos con el siguiente método en archivos de más de 20 MB )

Así que aquí está lo que hice:

 sort file1 > file1.sorted sort file2 > file2.sorted diff file1.sorted file2.sorted | grep "< " | sed 's/^< *//' > files.diff diff file1.sorted files.diff | grep "< " | sed 's/^< *//' > files.same.sorted 

Si “files.same.sorted” debe haber estado en el mismo orden que los originales, entonces agregue esta línea para el mismo orden que file1:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file1 > files.same

o, para el mismo orden que el archivo2:

awk 'FNR==NR {a[$0]=$0; next}; $0 in a {print a[$0]}' files.same.sorted file2 > files.same

Si los dos archivos aún no están ordenados, puede usar:

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

y funcionará, evitando el mensaje de error comm: file 2 is not in sorted order al hacer comm -12 a.txt b.txt .

Solo como referencia si alguien todavía está buscando cómo hacerlo para varios archivos, consulte la respuesta vinculada a Encontrar líneas que coincidan en muchos archivos.


Combinando estas dos respuestas ( ans1 y ans2 ), creo que puede obtener el resultado que necesita sin ordenar los archivos:

 #!/bin/bash ans="matching_lines" for file1 in * do for file2 in * do if [ "$file1" != "$ans" ] && [ "$file2" != "$ans" ] && [ "$file1" != "$file2" ] ; then echo "Comparing: $file1 $file2 ..." >> $ans perl -ne 'print if ($seen{$_} .= @ARGV) =~ /10$/' $file1 $file2 >> $ans fi done done 

Simplemente guárdelo, dele derechos de ejecución ( chmod +x compareFiles.sh ) y ejecútelo. Tomará todos los archivos presentes en el directorio de trabajo actual y hará que el resultado sea una comparación total que salga en el archivo “matching_lines”.

Cosas para mejorar:

  • Omitir directorios
  • Evite comparar todos los archivos dos veces (archivo1 frente a archivo2 y archivo2 frente a archivo1).
  • Tal vez agregue el número de línea al lado de la cadena correspondiente
 rm file3.txt cat file1.out | while read line1 do cat file2.out | while read line2 do if [[ $line1 == $line2 ]]; then echo $line1 >>file3.out fi done done 

Esto debería hacerlo.