Me gustaría realizar una operación diff
en 2 archivos y que cese ante la primera diferencia. No necesito que el comando se realice a través de diff
, por supuesto, pero sí que el comando real cese una vez que se encuentre e informe la primera diferencia. estoy corriendo en algunosmuyarchivos grandes y espera una coincidencia perfecta, pero aún quiere saber cuál es la diferencia, en caso de que se encuentre alguna, por lo que diff -q
, diff ... |head -1
y cmp
no son buenos. Y, dado que los archivos son muy grandes, sería bueno algo que no agote la memoria. Aunque no es necesario para mi problema actual, puntos de bonificación por soluciones que funcionan para las primeras n diferencias (especificadas por el usuario) y por aquellas que pueden ignorar las diferencias de espacios en blanco.
Respuesta1
cmp
se detiene en la primera diferencia:
% cat foo
foo
bar
baz
---
foo
bar
baz
% cat bar
foo
bar
baz
---
foo+
bar+
baz+
% cmp foo bar
foo bar differ: byte 20, line 5
%
Podrías envolverlo con un script para imprimir las diferentes líneas:
#! /bin/bash
line=$(cmp "$1" "$2" | awk '{print $NF}')
if [ ! -z $line ]; then
awk -v file="$1" -v line=$line 'NR==line{print "In file "file": "$0; exit}' "$1"
awk -v file="$2" -v line=$line 'NR==line{print "In file "file": "$0; exit}' "$2"
fi
% ./script.sh foo bar
In file foo: foo
In file bar: foo+
Parte del costo ahora se traslada a los comandos AWK, pero debería ser significativamente más rápido que verificar ambos archivos por completo.
Respuesta2
Probé esto con los casos triviales pero te dejo la prueba de campo:
$ cat f1
l1
l21 l22 l23 l24
l3
l4x
l5
$ cat f2
l1
l21 l22 l23
l3
l4y
l5
$ cat awkdiff.awk
BEGIN {
maxdiff = 5
ignoreemptylines = 1
whitespaceaware = 1
if (whitespaceaware) {
emptypattern = "^[[:space:]]*$"
} else {
emptypattern = "^$"
FS=""
}
f1 = ARGV[1]
f2 = ARGV[2]
rc1=rc2=1
while( (rc1>0 && rc2>0 && diff<maxdiff) ) {
rc1 = getline l1 < f1 ; ++nr1
rc2 = getline l2 < f2 ; ++nr2
if (ignoreemptylines) {
while ( l1 ~ emptypattern && rc1>0) {
rc1 = getline l1 < f1 ; ++nr1
}
while ( l2 ~ emptypattern && rc2>0) {
rc2 = getline l2 < f2 ; ++nr2
}
}
if ( rc1>0 && rc2>0) {
nf1 = split( l1, a1)
nf2 = split( l2, a2)
if ( nf1 <= nf2) {
nfmin = nf1
} else {
nfmin = nf2
}
founddiff = 0
for (i=1; i<=nfmin; ++i) {
if ( a2[i]"" != a1[i]"") {
printf "%d:%d:{%s} != %d:%d:{%s}\n", \
nr1, nf1, a1[i], nr2, nf2, a2[i]
founddiff=1
++diff
break
}
}
if ( !founddiff && nf1 != nf2) {
if ( nf1 > nf2)
printf "%d:%d:{%s} != %d:EOL\n", nr1, nfmin+1, a1[nfmin+1], nr2
else
printf "%d:EOL != %d:%d:{%s}\n", nr1, nr2, nfmin+1, a2[nfmin+1]
++diff
}
} else {
if ( rc1 == -1 && rc2 == -1) {
print "IO error"
} else if ( rc1 == 1 && rc2 == 0) {
print "%d:%s != EOL\n", nr1, l1
} else if ( rc1 == 0 && rc2 == 1) {
printf "EOL != %d:%s\n", nr2, l2
}
}
}
}
$ awk -f awkdiff.awk /tmp/f1 /tmp/f2
2:4:{l24} != 2:EOL
6:1:{l4x} != 5:1:{l4y}
maxdiff = N: establece el número máximo de diferencias en las que debe detenerse la comparación
ignoreemptylines = 1|0: especifica si las líneas vacías deben ignorarse al comparar
whitespaceaware = 1|0: especifica si la comparación debe realizarse por palabras (suponiendo que los espacios en blanco consecutivos sean iguales) o por líneas