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

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

Comment puis-je lancer diff sur les files de configuration, en sautant les commentaires? Une ligne commentée est définie par:

  • les espaces de début facultatifs (tabs et espaces)
  • signe de hachage ( # )
  • tout autre caractère

L'expression régulière (la plus simple) ignorant la première exigence serait #.* . J'ai essayé l' --ignore-matching-lines=RE ( -I RE ) de GNU diff 3.0, mais je ne pouvais pas le faire fonctionner avec cet ER. J'ai aussi essayé .*#.* Et .*\#.* Sans chance. Littéralement mettre la ligne ( Port 631 ) comme RE ne correspond à rien, ni aider à mettre le RE entre les barres obliques.

Comme suggéré dans "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 pour diff -I '#.*' file1 file2 .

Alors, comment cette option devrait-elle être utilisée? Comment puis-je faire diff sauter certaines lignes (dans mon cas, commentaires)? Veuillez ne pas suggérer de grep le file et de comparer les files temporaires.

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

Le test

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

  text 

Fichier test2:

  text #comment 

Fichier test3:

  changed text #comment 

Les commands:

 $ # 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 expliquant comment utiliser l'option -I correctement, je vais vous proposer une alternative qui fonctionne dans les shell bash:

 diff -u -B <(grep -vE '^\s*(#|$)' test1) <(grep -vE '^\s*(#|$)' test2) 
  • diff -u – diff unifié
    • -B – ignore les lignes vides
  • <(command) – une fonctionnalité bash appelée substitution de process qui ouvre un descripteur de file pour la command, ce qui supprime le besoin d'un file temporaire
  • grep – command pour l'printing de lignes (non) correspondant à un motif
    • -v – affiche les lignes non-appariées
    • E – utiliser 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 à des espaces (tabulations et espaces) le cas échéant
    • (#|$) correspondent à une marque de hachage, ou bien la fin d'une ligne

Essayer:

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

S'il vous plaît noter que l'expression régulière doit correspondre à la ligne correspondante dans les deux files et il correspond à chaque ligne modifiée dans le morceau pour fonctionner, sinon il affichera toujours la différence.

Utilisez des guillemets simples pour protéger le model du shell en expansion et pour échapper aux caractères réservés aux expressions rationnelles (par exemple, des parenthèses).

Nous pouvons lire dans le manuel diffutils :

Cependant, -I seulement l'insertion ou la suppression des 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'set complet des changements à proximité, y compris les changements ignorables. Vous pouvez spécifier plus d'une expression régulière pour les lignes à ignorer en utilisant plusieurs options -I . 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 .

J'ignore habituellement ce fouillis par l'un ou l'autre:

  • Génération de versions non commentées à l'aide de grep -v "^#" | cat -s grep -v "^#" | cat -s et en différant ceux ou …
  • Utiliser vim -d pour regarder les files. La mise en surbrillance de la syntaxe prend soin de rendre évidente les différences entre les commentaires et les non-commentaires. La mise en surbrillance diff de la différence en ligne pour que vous puissiez voir quelles valeurs ou parties de valeurs ont été changées en un coup d'oeil en font mon préféré.

Après la search sur le web, le path alternatif de Lekensteyn est le meilleur que j'ai trouvé.

Mais je veux utiliser la sortie dif en tant que patch … et il y a un problème parce que le numéro de ligne est noté à cause de "grep -v".

J'ai donc l'intention d'améliorer cette command line:

 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 file correctif.

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 

teste maintenant notre command

 $ 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 files produits par substitution de process. La ligne entre "+ nouvelle ligne" et "-autre text" est le caractère d'espace par défaut défini dans notre expression sed pour replace les commentaires.

Et maintenant, que se passe-t-il lorsque nous appliquons ce correctif:

 $ 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 un 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 file de travail de file de correction (sans garantie de résultat dans le process de diff très complexe).

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 lignes vides:

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

ou vous pouvez faire

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