Wie kann ich Dateien unterscheiden, die Kommentare ignorieren (Zeilen, die mit # beginnen)?

55

Ich habe zwei Konfigurationsdateien, das Original aus dem Paketmanager und eine von mir angepasste. Ich habe einige Kommentare hinzugefügt, um das Verhalten zu beschreiben.

Wie kann ich diffdie Konfigurationsdateien ausführen und die Kommentare überspringen? Eine kommentierte Zeile wird definiert durch:

  • optionales führendes Leerzeichen (Tabulatoren und Leerzeichen)
  • Hash-Zeichen ( #)
  • alles andere Zeichen

Der (einfachste) reguläre Ausdruck, der die erste Anforderung überspringt, wäre #.*. Ich habe die --ignore-matching-lines=RE( -I RE) -Option von GNU diff 3.0 ausprobiert , aber ich konnte sie mit dieser RE nicht zum Laufen bringen. Ich habe es auch versucht .*#.*und .*\#.*ohne Glück. Das buchstäbliche Setzen von line ( Port 631) als REpasst zu nichts und das Setzen des RE zwischen Schrägstriche hilft auch nicht.

Wie in "diff" vorgeschlagen, scheint der Geschmack von Regex zu fehlen? Ich habe versucht grep -G:

grep -G '#.*' file

Dies scheint mit den Kommentaren übereinzustimmen, aber es funktioniert nicht für diff -I '#.*' file1 file2.

Wie soll diese Option verwendet werden? Wie kann ich diffbestimmte Zeilen überspringen lassen (in meinem Fall Kommentare)? Bitte schlagen grepSie die Datei nicht vor und vergleichen Sie die temporären Dateien nicht.

Lekensteyn
quelle
12
Die -IOption bewirkt, dass ein Block nur dann ignoriert wird, wenn alle Zeilen mit dem regulären Ausdruck übereinstimmen. Auf diese Weise können Sie nur eine Kommentaränderung ignorieren, nicht jedoch die Kommentaränderungen, die sich in der Nähe einer Nichtkommentaränderung befinden.
Gilles 'SO - hör auf, böse zu sein'
@Gilles: Danke, jetzt verstehe ich, warum diff -Isich nicht so verhält, wie ich es erwartet habe. Ich habe meine Antwort mit einem Beispiel aktualisiert, das dieses Verhalten für mich verdeutlicht.
Lekensteyn

Antworten:

49

Laut Gilles -Iignoriert die Option eine Zeile nur, wenn nichts anderes in diesem Satz übereinstimmt, mit Ausnahme der Übereinstimmung von -I. Ich habe es nicht vollständig verstanden, bis ich es getestet habe.

Der Test

Drei Dateien werden in meinem Test beteiligt:
Datei test1:

    text

Datei test2:

    text
    #comment

Datei test3:

    changed text
    #comment

Die Befehle:

$ # comparing files with comment-only changes
$ diff -u -I '#.*' test{1,2}
$ # comparing files with both comment and regular changes
$ diff -u -I '#.*' test{2,3}
--- test2       2011-07-20 16:38:59.717701430 +0200
+++ test3       2011-07-20 16:39:10.187701435 +0200
@@ -1,2 +1,2 @@
-text
+changed text
 #comment

Der alternative Weg

Da es bisher keine Antwort gibt, die erklärt, wie die -IOption richtig verwendet wird, werde ich eine Alternative anbieten, die in Bash-Shells funktioniert:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - Unified Diff
    • -B - Leerzeilen ignorieren
  • <(command)- Eine Bash-Funktion namens Prozessersetzung, die einen Dateideskriptor für den Befehl öffnet. Dadurch ist keine temporäre Datei mehr erforderlich
  • grep - Befehl zum Drucken von Zeilen (nicht) passend zu einem Muster
    • -v - Nicht übereinstimmende Zeilen anzeigen
    • E - Verwenden Sie erweiterte reguläre Ausdrücke
    • '^\s*(#|$)' - Ein regulärer Ausdruck, der mit Kommentaren und Leerzeilen übereinstimmt
      • ^ - Stimmt mit dem Zeilenanfang überein
      • \s* - Passen Sie ggf. Leerzeichen (Tabulatoren und Leerzeichen) an
      • (#|$) Entspricht einer Raute oder alternativ dem Ende einer Zeile
Lekensteyn
quelle
6

Versuchen:

diff -b -I '^#' -I '^ #' file1 file2

Bitte beachten Sie, dass der reguläre Ausdruck mit der entsprechenden Zeile in beiden Dateien übereinstimmen muss und mit jeder geänderten Zeile im Block übereinstimmt, damit er funktioniert. Andernfalls wird der Unterschied weiterhin angezeigt.

Verwenden Sie einfache Anführungszeichen, um das Muster vor dem Expandieren der Shell zu schützen und die durch Regex reservierten Zeichen (z. B. Klammern) zu umgehen.

Wir können im diffutilsHandbuch lesen :

-IIgnoriert jedoch das Einfügen oder Löschen von Zeilen, die den regulären Ausdruck enthalten , nur, wenn jede geänderte Zeile im Block (jede Einfügung und jede Löschung) mit dem regulären Ausdruck übereinstimmt.

Mit anderen Worten, diffdruckt für jede nicht ignorierbare Änderung den vollständigen Satz von Änderungen in seiner Umgebung, einschließlich der ignorierbaren. Sie können mehrere reguläre Ausdrücke für zu ignorierende Zeilen angeben, indem Sie mehrere -IOptionen verwenden. diffversucht, jede Zeile mit jedem regulären Ausdruck abzugleichen, wobei mit dem zuletzt angegebenen begonnen wird.

Dieses Verhalten wird auch hier von armel gut erklärt .

Verwandte Themen: Wie kann ich ein Diff ausführen, bei dem alle Kommentare ignoriert werden?

Kenorb
quelle
2

Nach der Suche im Internet ist der alternative Weg von Lekensteyn der bessere, den ich gefunden habe.

Aber ich möchte die dif-Ausgabe als Patch verwenden ... und es gibt ein Problem, da die Zeilennummern aufgrund von "grep -v" beibehalten werden.

Also habe ich vor, diese Kommandozeile zu verbessern:

diff -u -B <(sed 's/^[[:blank:]]*#.*$/ /' file1)  <(sed 's/^[[:blank:]]*#.*$/ /' file2)

Es ist nicht perfekt, aber die Zeilennummer bleibt in der Patch-Datei erhalten.

Wenn jedoch anstelle der Kommentarzeile eine neue Zeile hinzugefügt wird, wird beim Patchen ein Hunk FAILED erzeugt, wie unten zu sehen ist.

File test1:
  text
  #comment
  other text
File test2:
  text
  new line here
  #comment changed
  other text changed

Testen Sie jetzt unser Kommando

$ echo -e "#!/usr/bin/sed -f\ns/^[[:blank:]]*#.*$/ /" > outcom.sed
$ echo "diff -u -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ chmod +x mydiff.sh outcom.sed
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
--- /dev/fd/63  2014-08-23 10:05:08.000000000 +0200
+++ /dev/fd/62  2014-08-23 10:05:08.000000000 +0200
@@ -1,2 +1,3 @@
 text
+new line

-other text
+other text changed

/ dev / fd / 62 & / dev / fd / 63 sind durch Prozessersetzung erzeugte Dateien. Die Zeile zwischen "+ Neue Zeile" und "-anderen Text" ist das Standard-Leerzeichen, das in unserem sed-Ausdruck definiert ist, um Kommentare zu ersetzen.

Und jetzt, was kommt, wenn wir diesen Patch anwenden:

$ patch -p0 file1 < file.dif 
patching file file1
Hunk #1 FAILED at 1.
1 out of 1 hunk FAILED -- saving rejects to file file1.rej

Die Lösung besteht darin, das Unified Diff-Format nicht ohne -u zu verwenden

$ echo "diff -B <(./outcom.sed \$1)  <(./outcom.sed \$2)" > mydiff.sh
$ ./mydiff.sh file1 file2 > file.dif
$ cat file.dif
1a2
> new line
3c4
< other text
---
> other text changed
$ patch -p0 file1 < file.dif 
patching file file1
$ cat file1
text
new line
#comment
other text changed

Jetzt Patch-Datei Arbeitsdatei (ohne Gewähr für Ergebnis in sehr komplexen Diff-Prozess).

syjust
quelle
Ihr Unified Diff wird aufgrund der Kontextunterschiede nicht angewendet. Mit können Sie diff -U0 one twoden Kontext deaktivieren. Zum Patchen gibt es eine Reihe von Tools, die möglicherweise besser geeignet sind, wie z. B. kdiff3.
Lekensteyn
Vielen Dank für die -U0Option, den Kontext zu deaktivieren. Hinweis: kdiff3 ist ein grafisches Tool. Ich benötige ein automatisches Tool zum Verwalten von Git-Merge-Attributen.
Syjust
vimdiffunterstützt Drei-Wege-Zusammenführungen, könnte einen Blick wert sein.
Lekensteyn
Um genauer zu sein, benötige ich ein Skript-Tool zum Automatisieren des Git-Merge-Prozesses mit Ausschlüssen in einem SQL-Skript. kdiff3 und vimdiff sind interaktive Tools, die in meinem Fall nicht verwendet werden können.
Syjust
1

Normalerweise ignoriere ich diese Unordnung entweder:

  • Generieren von nicht kommentierten Versionen unter Verwendung grep -v "^#" | cat -sund Unterscheidung dieser oder ...
  • Verwenden Sie vim -d, um die Dateien zu betrachten. Die Syntaxhervorhebung sorgt dafür, dass Unterschiede zwischen Kommentaren und Nichtkommentaren deutlich werden. Die diff-Hervorhebung der Inline-Differenz, sodass Sie auf einen Blick sehen können, welche Werte oder Teile von Werten geändert wurden, macht dies zu meinem Favoriten.
Caleb
quelle
0

Hier ist, was ich benutze, um alle kommentierten Zeilen zu entfernen - auch die mit einem Tabulator oder Leerzeichen beginnenden - und die leeren:

egrep -v "^$|^[[:space:]]*#" /path/to/file

oder du kannst tun

sed -e '/^#.*/d' -e 's/#.*//g' | cat -s
Philomath
quelle