vacio - modificar archivo en unix
Comando Unix para encontrar lĂneas comunes en dos archivos (11)
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)
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
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 recopilar 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!
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!
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.
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
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 programas 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.
awk ''NR==FNR{a[$1]++;next} a[$1] '' file1 file2
perl -ne ''print if ($seen{$_} .= @ARGV) =~ /10$/'' file1 file2
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.