Comment différencier les fichiers en ignorant les commentaires (lignes commençant par #)?

55

J'ai deux fichiers de configuration, l'original du gestionnaire de paquets et un personnalisé modifié par moi-même. J'ai ajouté quelques commentaires pour décrire le comportement.

Comment puis-je exécuter diffsur les fichiers de configuration, en ignorant les commentaires? Une ligne commentée est définie par:

  • espaces optionnels principaux (onglets et espaces)
  • signe de hachage ( #)
  • autre chose

L'expression régulière (la plus simple) ignorant la première condition serait #.*. J'ai essayé l' option --ignore-matching-lines=RE( -I RE) de GNU diff 3.0, mais je ne pouvais pas la faire fonctionner avec cette RE. J'ai aussi essayé .*#.*et .*\#.*sans chance. Mettre littéralement la ligne ( Port 631) comme REne correspond à rien, cela n'aide pas non plus de mettre le RE entre les barres obliques.

Comme suggéré dans l' outil “diff”, la saveur de regex semble manquer? , J'ai essayé grep -G:

grep -G '#.*' file

Cela semble correspondre aux commentaires, mais cela ne fonctionne pas diff -I '#.*' file1 file2.

Alors, comment utiliser cette option? Comment puis-je faire diffsauter certaines lignes (dans mon cas, les commentaires)? Ne suggérez pas greple fichier et ne comparez pas les fichiers temporaires.

Lekensteyn
la source
12
Cette -Ioption ne permet d’ignorer un bloc que si toutes ses lignes correspondent à l’expression rationnelle. Ainsi, vous pouvez ignorer un changement de commentaire uniquement de cette manière, mais pas les changements de commentaire qui se rapprochent d'un changement sans commentaire.
Gilles 'SO- arrête d'être méchant'
@ Gilles: Merci, maintenant je comprends pourquoi je diff -Ine me comporte pas comme prévu. J'ai mis à jour ma réponse avec un exemple qui clarifiait ce comportement pour moi.
Lekensteyn

Réponses:

49

Selon Gilles, l' -Ioption n'ignore une ligne que si rien d'autre à l'intérieur de cet ensemble ne correspond sauf à la correspondance de -I. Je ne l'ai pas complètement compris avant de l'avoir testé.

Le test

Trois fichiers sont impliqués dans mon test:
Fichier test1:

    text

Fichier test2:

    text
    #comment

Fichier test3:

    changed text
    #comment

Les commandes:

$ # 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

La voie alternative

Comme il n'y a pas de réponse à ce jour pour expliquer comment utiliser l' -Ioption correctement, je fournirai une alternative qui fonctionne dans les shells bash:

diff -u -B <(grep -vE '^\s*(#|$)' test1)  <(grep -vE '^\s*(#|$)' test2)
  • diff -u - diff unifié
    • -B - ignorer les lignes vides
  • <(command)- une fonctionnalité bash appelée substitution de processus qui ouvre un descripteur de fichier pour la commande, cela évite d'avoir à utiliser un fichier temporaire
  • grep - commande pour imprimer des lignes (ne) correspondant pas à un motif
    • -v - montrer les lignes qui ne correspondent pas
    • E - utiliser des expressions régulières étendues
    • '^\s*(#|$)' - une expression régulière correspondant aux commentaires et aux lignes vides
      • ^ - correspondre au début d'une ligne
      • \s* - faire correspondre les espaces (tabulations et espaces) le cas échéant
      • (#|$) correspondre à une marque de hachage, ou bien à la fin d'une ligne
Lekensteyn
la source
6

Essayer:

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

Notez que l'expression rationnelle doit correspondre à la ligne correspondante dans les deux fichiers et à chaque ligne modifiée dans le bloc pour fonctionner, sinon la différence sera toujours affichée.

Utilisez des guillemets simples pour protéger le motif de l’extension du shell et pour échapper aux caractères réservés aux expressions rationnelles (par exemple, les crochets).

Nous pouvons lire dans le diffutilsmanuel:

Cependant, -Iignore uniquement l'insertion ou la suppression de lignes contenant l'expression régulière si chaque ligne modifiée dans le bloc (chaque insertion et chaque suppression) correspond à l'expression régulière.

En d'autres termes, pour chaque changement non-ignorable, diffimprime l'ensemble complet des changements dans son voisinage, y compris ceux qui sont ignorables. Vous pouvez spécifier plusieurs expressions régulières à ignorer en utilisant plusieurs -Ioptions. difftente de faire correspondre chaque ligne à chaque expression régulière, en commençant par la dernière donnée.

Ce comportement est également bien expliqué par armel ici .

Connexes: Comment puis-je effectuer un diff qui ignore tous les commentaires?

Kenorb
la source
2

Après des recherches sur le Web, le meilleur moyen que j'ai trouvé est le Lekensteyn.

Mais je veux utiliser dif output comme patch ... et il y a un problème car les numéros de ligne sont conservés note à cause de "grep -v".

J'ai donc l'intention d'améliorer cette ligne de commande:

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

Ce n'est pas parfait, mais les numéros de ligne sont conservés dans un fichier de correctif.

Cependant, si une nouvelle ligne est ajoutée à la place d'une ligne de commentaire, le commentaire produira un Hunk FAILED lors de l'application du correctif, comme indiqué ci-dessous.

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

tester maintenant notre commande

$ 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 et / dev / fd / 63 sont des fichiers produits par substitution de processus. La ligne entre "+ nouvelle ligne" et "-autre texte" est le caractère d'espace par défaut défini dans notre expression sed pour remplacer les commentaires.

Et maintenant, qu'est-ce qui arrive quand on applique ce patch:

$ 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

La solution est de ne pas utiliser le format diff unifié sans -u

$ 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

maintenant fichier de travail fichier de correctif (sans garantie de résultat dans le processus diff très complexe).

syjust
la source
Votre diff unifié ne parvient pas à s'appliquer en raison des différences de contexte. Vous pouvez utiliser diff -U0 one twopour désactiver le contexte. Pour le correctif, il existe de nombreux outils mieux adaptés, tels que kdiff3.
Lekensteyn
Merci pour l' -U0option de désactiver le contexte. Remarque: kdiff3 est un outil graphique. J'ai besoin d'un outil automatique pour gérer les attributs de fusion Git.
syjust
vimdiffprend en charge les fusions à trois voies, pourrait être intéressant de regarder.
Lekensteyn
pour être plus précis, j'ai besoin d'un outil de script pour automatiser le processus de fusion git avec exclure dans un script SQL. kdiff3 et vimdiff sont des outils interactifs, non utilisables dans mon cas.
syjust
1

J'ignore généralement ce fouillis de l'une des manières suivantes:

  • Générer des versions non commentées en utilisant grep -v "^#" | cat -set en diff ...
  • Utiliser vim -dpour regarder les fichiers. La mise en évidence de la syntaxe permet de rendre évidentes les différences entre commentaires et non-commentaires. La mise en surbrillance de la différence en ligne de sorte que vous puissiez voir quelles valeurs ou parties de valeurs ont été modifiées en un coup d'œil en fait mon préféré.
Caleb
la source
0

Voici ce que j'utilise pour supprimer toutes les lignes commentées, même celles commençant par une tabulation ou un espace, et les vides:

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

ou tu peux faire

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