diff stop após a primeira diferença

3

Gostaria de executar um diff em 2 arquivos e fazer com que ele pare na primeira diferença. Eu não exijo que o comando seja feito via diff , é claro, mas eu preciso que o comando real cesse uma vez que a primeira diferença seja encontrada e relatada. Estou executando em alguns arquivos muito grandes e espero uma correspondência perfeita, mas ainda quero saber qual era a diferença, caso um deles fosse encontrado, então diff -q , diff ... |head -1 e cmp não são bons. E, como os arquivos são muito grandes, algo que não esgota a memória seria bom. Embora não seja necessário para o meu problema atual, os pontos de bônus para soluções que trabalham para o primeiro (especificado pelo usuário) n diferenças, e para aqueles que podem ignorar as diferenças de espaço em branco.

    
por TTT 12.02.2016 / 22:50

2 respostas

2

cmp para na primeira diferença:

% 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
% 

Você pode envolver um script em torno dele para imprimir as diferentes linhas:

#! /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 do custo é agora transferido para os comandos do AWK, mas deve ser significativamente mais rápido do que verificar os dois arquivos completamente.

    
por 13.02.2016 / 01:44
1

Eu testei isso com os casos triviais, mas deixei o teste de campo para você:

$ 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: define o número máximo de diferenças nas quais a comparação deve parar

ignoreemptylines = 1 | 0: especifica se linhas vazias devem ser ignoradas ao comparar

whitespaceaware = 1 | 0: especifica se a comparação deve ser feita no sentido horário (supondo que espaços em branco consecutivos sejam iguais) ou linearmente

    
por 13.02.2016 / 13:31