Diff Kopf der Dateien

11

Ich habe zwei Dateien. Ich vermute, eine Datei ist eine Teilmenge der anderen. Gibt es eine Möglichkeit, die Dateien zu unterscheiden, um (auf prägnante Weise) zu identifizieren, wo in die erste Datei die zweite Datei passt?

Richard
quelle
Meinen Sie damit, dass die Zeilen einer Datei eine Teilsequenz der anderen oder tatsächlich eine zusammenhängende Teilzeichenfolge sind?
Kaz
Ein zusammenhängender Teilstring, @Kaz.
Richard

Antworten:

14

diff -e bigger smaller wird den Trick machen, erfordert aber einige Interpretation, da die Ausgabe ein "gültiges ed-Skript" ist.

Ich habe zwei Dateien erstellt, "größer" und "kleiner", wobei der Inhalt von "kleiner" mit den Zeilen 5 bis 9 von "größer" identisch ist, wobei "diff -e größer kleiner" mich dazu gebracht hat:

% diff -e bigger smaller
10,15d
1,4d

Dies bedeutet "Löschen Sie die Zeilen 10 bis 15 von" größer "und dann die Zeilen 1 bis 4, um" kleiner "zu werden". Das heißt, "kleiner" sind die Zeilen 5 bis 9 von "größer".

Das Umkehren der Dateinamen hat mich etwas komplizierter gemacht. Wenn "kleiner" wirklich eine Teilmenge von "größer" darstellt, werden in der Ausgabe nur "d" -Befehle (zum Löschen) angezeigt.

Bruce Ediger
quelle
5

Sie können dies visuell mit meld tun . Leider ist es ein GUI-Tool, aber wenn Sie dies nur einmal und in einer relativ kleinen Datei tun möchten, sollte es in Ordnung sein:

Das Bild unten ist die Ausgabe von meld a b:

Geben Sie hier die Bildbeschreibung ein

terdon
quelle
1
Meld ist nett, aber mit Dateien mit mehr als 100 MB funktioniert es nicht ganz so gut.
Richard
@ Richard nein, tut es nicht und ich würde sowieso ein Kommandozeilen-Tool bevorzugen, ich dachte nur, ich würde es erwähnen.
Terdon
Sieht sehr ähnlich aus vimdiff, was im Terminal verfügbar ist.
Patrick
2

Wenn die Dateien klein genug sind, können Sie beide in Perl schlürfen und die Regex-Engine den Trick ausführen lassen:

perl -0777e '
        open "$FILE1","<","file_1";
        open "$FILE2","<","file_2";
        $file_1 = <$FILE1>;
        $file_2 = <$FILE2>;
        print "file_2 is", $file_1 =~ /\Q$file_2\E/ ? "" : "not";
        print " a subset of file_1\n";
'

Der -0777Switch weist Perl an, das Trennzeichen $/für Eingabedatensätze auf den undefinierten Wert zu setzen, um Dateien vollständig zu verschlingen.

Joseph R.
quelle
1
Was macht 777das Ich nehme an, Sie übergeben NULL, $/aber warum? Auch da dies irgendwie esoterische Schalter sind, wäre eine Erklärung für die Nicht-Perl-Leute nett.
Terdon
1
@terdon Ich mache es in der Tat, um die Dateien als Ganzes zu schlürfen. Erklärung hinzugefügt.
Joseph R.
Aber warum ist das notwendig? $a=<$fh>sollte sowieso schlürfen oder?
Terdon
1
@terdon Nicht das ich wüsste, nein. Standardmäßig $/ist so eingestellt, \ndass $a=<$fh>nur eine Zeile der Datei gelesen wird, für die $fhgeöffnet wurde. Es sei denn natürlich perl, das Befehlszeilenverhalten hat andere Standardeinstellungen, die mir nicht bekannt sind.
Joseph R.
Argh, ja, mein schlechtes, ich schlürfe fast nie Dateien oder benutze die while $foo=<FILE>Redewendung, also war ich mir nicht sicher und führte einen (falschen) Test durch, der zu funktionieren schien. Keine Ursache :).
Terdon
1

Wenn es sich bei den Dateien um Textdateien handelt und smallerinnerhalb von biggeram Anfang einer Zeile beginnt, ist die Implementierung nicht allzu schwierig mit awk:

awk -v i=0 'NR==FNR{l[n++]=$0;next}
    {if ($0 == l[i]) {if (++i == n) {print FNR-n+1;exit}} else i=0}
    ' smaller bigger
Stéphane Chazelas
quelle
1

Ihre Frage lautet "Diff head of files". Wenn Sie wirklich meinen, dass eine Datei der Kopf der anderen ist, dann cmpsagt Ihnen eine einfache Datei Folgendes:

cmp big_file small_file
cmp: EOF on small_file

Dies zeigt Ihnen, dass ein Unterschied zwischen den beiden Dateien erst erkannt wurde, als beim Lesen das Dateiende erreicht wurde small_file.

Wenn Sie jedoch meinen, dass der gesamte Text einer kleinen Datei irgendwo im Inneren vorkommen kann big_file, können Sie verwenden, vorausgesetzt, Sie können beide Dateien in den Speicher einpassen

perl -le '
   use autodie;
   undef $/;
   open SMALL, "<", "small_file";
   open BIG, "<", "big_file";
   $small = <SMALL>;
   $big = <BIG>;
   $pos = index $big, $small;
   print $pos if $pos >= 0;
'

Dadurch wird der Versatz gedruckt, in big_filedem sich der Inhalt von small_filebefindet (z. B. 0, wenn er small_filezu Beginn übereinstimmt big_file). Wenn small_filees innen nicht übereinstimmt big_file, wird nichts gedruckt. Wenn ein Fehler auftritt, ist der Exit-Status ungleich Null.

jrw32982 unterstützt Monica
quelle