Commande Sed qui ignorerait toute correspondance commentée

J'essaye de créer une command sed en utilisant regex afin de substituer quelque chose dans un file text seulement si ce n'est pas commenté mais je me heurte à quelques problèmes en raison de ma connaissance presque inexistante des commands de sed.

J'ai trouvé des solutions pour de petites parties du problème, mais certaines ne sont pas assez complètes ou je ne peux pas les assembler. TL version DR disponible à la fin.

Déterminons d'abord mon but ultime

Je voudrais faire correspondre n'importe quoi (comme toute regex régulière (hehe)) dans un file text seulement si ce n'est pas commenté. Comme je voudrais le faire pour plusieurs langues, prenons juste les commentaires de C communs.

Donc, dans ce cas, les mots ou les lignes peuvent être commentés de différentes façons. Nous avons le // pour commenter seulement ce qui est suivant sur la ligne et nous avons aussi le bloc /* */ comment.


Environnement

Je travaille actuellement sur Mac OSX qui ne supporte que POSIX sed mais j'ai installé un GNU-sed que je trouve meilleur. (Merci à Homebrew le packageage est gnu-sed et la command est gsed .) Donc, les deux sont à ma disposition si vous préférez utiliser l'un ou l'autre.

J'écris ceci en supposant qu'un GNU-sed est utilisé.


Ignorer un cas

Premier problème, comment ignorer certains cas. Je l'ai trouvé assez facilement dans ce sujet .

Maintenant, la // partie me semble facile et je n'aurais qu'à append une condition OR ( | ) pour la joindre à l'autre condition.

Cela ressemblerait à quelque chose comme ceci:

  sed -E "/\/\/.*/! s/foo/bar/" file 

Ensuite, si le file d'input est:

 foo 42 test //foo //42 // foo //something foo foo 42 something foo foo 

La sortie est:

 bar 42 test //foo //42 // foo //something foo bar 42 something bar bar 

Alors maintenant, je vais juste concentrer ma reflection sur le bloc /* */ comment.


Correspondance par plusieurs lignes

Deuxième problème, comment faire correspondre le regex par plusieurs lignes. Eh bien, je pense que c'est le problème majeur. J'ai trouvé ce sujet parler de comment faire correspondre à travers un seul nouveau caractère de ligne. Eh bien, il m'a fallu un moment pour comprendre comment cela fonctionne. Mais cette partie de la solution m'apporte un nouveau problème et de nouvelles questions.

Il ne peut évidemment ignorer qu'une seule nouvelle ligne ( \n ). Donc, je veux maintenant faire la même chose pour un nombre de lignes inconnu (de 0 à infini ( * )). Je parie que je dois parcourir les lignes mais c'est là que je suis coincé parce que je ne sais rien des commands de sed et c'est vraiment gênant pour moi.

Lors de mes searchs, j'ai trouvé un petit script ayant pour but de replace la command tail et il utilise une boucle (je suppose) mais je n'arrive pas à comprendre son fonctionnement.

Faites en sorte qu'il ne corresponde qu'à la */ partie

La troisième partie serait de s'assurer que le cas ignoré ne correspond qu'à des choses avant la fin du bloc de commentaire ( */ ). Donc, à la fin, le cas d'ignorer ne correspondrait qu'aux choses entre /* et */ . La command finale ignorerait complètement les choses écrites dans un bloc de commentaires.

Je n'ai fait aucune search réelle sur cette partie car je n'ai pas résolu le point précédent et il me semble que ce problème dépend du problème précédent.


Partie finale: Mettre tout cela set

Eh bien, il est évident que j'ai complètement échoué en ce moment.


TL; DR

Ma question est: Quelle serait la command sed afin de substituer tout ce que nous voulons dans un file text seulement si ce n'est pas commenté?


annexe

Si vous connaissez un moyen plus facile de le faire, en utilisant n'importe quelle autre langue, c'est également le bienvenu. Donc, si vous savez comment le faire avec awk , python ou n'importe quoi d'autre, n'hésitez pas à le partager.

Vous ne devriez pas les croire s'ils vous disent que cela ne peut pas être fait. Vous devriez les croire, cependant, s'ils vous disent que ce n'est pas facile.

 sed '\|*/|!{ s|/\*|\n&| #if ! */ repl 1st /* w/ \n/* h; s|foo|bar|g;/\n/!b #hold; repl all foo/bar; if ! \n branch G; s|\n.*\n||;:n #Get; clear difference; :new label n; \|*/|!bn;s|^|\n/*| #new line; if ! */ branch new label };s|*/|\n&|g #repl all */ w/ \n*/ s|foo|&\nbar|g;:r #repl all foo w/ foo\nbar s|\(/\*[^\n]*\)\nbar|\1|g;tr #repl all /*[^\n]*\nbar w/ foo s|foo\n\(b\)|\1|g #repl all foo\nbar w/ bar s|^\n/.||;s|\n||g #clear any \n inserts ' <<\INPUT asfoo /* asdfooasdfoo asdfasdfoo asdfasdfoo foo */foo /*foo*/ foo /*. foo*/ foo hello INPUT 

SORTIE

 asbar /* asdfooasdfoo asdfasdfoo asdfasdfoo foo */bar /*foo*/ bar /*. foo*/ bar hello