web-dev-qa-db-fra.com

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

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

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

  • espaces blancs facultatifs (tabulations et espaces)
  • signe de hachage (#)
  • tout autre personnage

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

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

grep -G '#.*' file

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

Alors, comment utiliser cette option? Comment puis-je faire diff sauter certaines lignes (dans mon cas, les commentaires)? Veuillez ne pas suggérer greping le fichier et comparer les fichiers temporaires.

56
Lekensteyn

Selon Gilles, l'option -I N'ignore une ligne que si rien d'autre à l'intérieur de cet ensemble ne correspond à l'exception de 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

Puisqu'il n'y a pas de réponse pour l'instant expliquant comment utiliser correctement l'option -I, Je vais fournir 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 fonction bash appelée substitution de processus qui ouvre un descripteur de fichier pour la commande, cela supprime le besoin d'un fichier temporaire
  • grep - commande pour imprimer des lignes correspondant (non) à un motif
    • -v - affiche les lignes qui ne correspondent pas
    • E - utilise des expressions régulières étendues
    • '^\s*(#|$)' - une expression régulière correspondant aux commentaires et aux lignes vides
      • ^ - correspond au début d'une ligne
      • \s* - correspond aux espaces (tabulations et espaces) le cas échéant
      • (#|$) Correspond à une marque de hachage ou à la fin d'une ligne
54
Lekensteyn

Essayer:

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

Veuillez noter que l'expression régulière doit correspondre à la ligne correspondante dans les deux fichiers et qu'elle correspond à chaque ligne modifiée dans le morceau pour fonctionner, sinon elle affichera toujours la différence.

Utilisez des guillemets simples pour protéger le modèle contre l'expansion de Shell et pour échapper aux caractères réservés aux expressions régulières (par exemple, les crochets).

Nous pouvons lire dans le manuel diffutils :

Cependant, -I ignore uniquement l'insertion ou la suppression de lignes qui contiennent l'expression régulière si chaque ligne modifiée dans le morceau (chaque insertion et chaque suppression) correspond à l'expression régulière.

En d'autres termes, pour chaque changement non ignorable, diff imprime l'ensemble complet des changements à son voisinage, y compris les ignorables. Vous pouvez spécifier plusieurs expressions régulières pour les lignes à ignorer en utilisant plusieurs -I option. diff essaie 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?

7
kenorb

Après une recherche sur le Web, le meilleur moyen de Lekensteyn est le meilleur que j'ai trouvé.

Mais je veux utiliser la sortie dif comme patch ... et il y a un problème car les numéros de ligne sont conservés à 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 le numéro de ligne est conservé dans le fichier patch.

Cependant, si une nouvelle ligne est ajoutée à la place de la ligne de commentaire ... le commentaire produira un Hunk FAILED lors du patch comme nous pouvons le voir 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 &/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 lorsque nous appliquons 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 consiste à 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 patch (sans garantie de résultat dans un processus de diff très complexe).

3
syjust

J'ignore généralement cet encombrement par:

  • Génération de versions non commentées à l'aide de grep -v "^#" | cat -s et en les différenciant ou ...
  • En utilisant vim -d pour consulter les fichiers. La mise en évidence de la syntaxe prend soin de rendre les différences entre commentaires et non-commentaires assez évidentes. La mise en évidence diff de la différence en ligne afin 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é.
1
Caleb

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

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

ou vous pouvez faire

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