Diff-Stopp nach dem ersten Unterschied

Diff-Stopp nach dem ersten Unterschied

Ich möchte einen diffauf 2 Dateien ausführen und ihn beim ersten Unterschied beenden. Ich verlange diffnatürlich nicht, dass der Befehl über ausgeführt wird, aber ich verlange, dass der eigentliche Befehl beendet wird, sobald der erste Unterschied gefunden und gemeldet wurde. Ich verwende einigesehrgroße Dateien und erwarte eine perfekte Übereinstimmung, möchte aber trotzdem wissen, was der Unterschied war, falls einer gefunden wird, also sind diff -q, diff ... |head -1, und cmpnicht gut. Und da die Dateien sehr groß sind, wäre etwas nett, das den Speicher nicht erschöpft. Obwohl es für mein aktuelles Problem nicht notwendig ist, gibt es Bonuspunkte für Lösungen, die für die ersten (vom Benutzer angegebenen) n Unterschiede funktionieren, und für solche, die Leerzeichenunterschiede ignorieren können.

Antwort1

cmpbleibt beim ersten Unterschied stehen:

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

Sie könnten ein Skript darum herum packen, um die verschiedenen Zeilen auszudrucken:

#! /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+

Ein Teil des Aufwands wird nun auf die AWK-Befehle verlagert, es sollte jedoch deutlich schneller sein, als wenn beide Dateien vollständig überprüft würden.

Antwort2

Ich habe dies mit den trivialen Fällen getestet, überlasse den Praxistest aber Ihnen:

$ 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: legt die maximale Anzahl von Unterschieden fest, bei der der Vergleich beendet werden soll

ignoreemptylines = 1|0: gibt an, ob leere Zeilen beim Vergleichen ignoriert werden sollen

whitespaceaware = 1|0: gibt an, ob der Vergleich wortweise (unter der Annahme, dass aufeinanderfolgende Leerzeichen gleich sind) oder zeilenweise erfolgen soll

verwandte Informationen