Forum Linux.général Motif multi-lignes avec 'sed'

Posté par  .
Étiquettes : aucune
0
9
déc.
2005
Bonjour

Je connais bien Perl et ses expressions régulières, mais j'utilise depuis quelques temps 'sed' quand j'ai besoin de faire des traitements simples.

Je rencontre quelques soucis avec l'utilisation d'expressions régulières multi-lignes avec sed.

Si j'ai bien compris, sed traite les lignes une par une, donc je peux faire tous les motifs de la Terre, je n'aurais jamais un remplacement de plusieurs lignes pour une seule.

ex: s/toto\ntiti/tata/

Y'a-t-il une syntaxe *simple* pour effectuer ce genre de remplacement avec sed ?

Merci d'avance
  • # multi lignes

    Posté par  (site web personnel) . Évalué à 3.

    sed traite les caractères qui se trouve dans son buffer. Par défaut, ce buffer contient toutes les lignes une par une.
    Mais tu peux rajouter des lignes dans le buffer, avec par exemple la commande N qui va rajouter la ligne suivante.

    Pour ton exemple, tu peux faire un truc du genre :

    sed 'N;s/toto\ntiti/tata/'
    • [^] # Re: multi lignes

      Posté par  . Évalué à 1.

        Merci pour la réponse, ca correspond tout à fait à ma question, mais il s'avère qu'en fait mon problème est un peu plus compliqué :)
        Cette solution fonctionne très bien, pour 1 remplacement sur un flux, mais mon traitement comporte n remplacements, certains sur 2 ou 3 lignes, d'autres sur 1 seule (la majorité). Apparement, l'utilisation de la fonction N induit des décalages dans la lecture du buffer et sed 'oublie' certaines occurences.
        Pour être plus simple, je vais vous donner un exemple concret de mes traitements. Je cherche à traduire des fichiers Dia ( http://www.gnome.org/projects/dia/ ) à la volée avec sed.
        J'ai donc des fichiers Dia, en français, et je souhaite les passer en anglais. Les fichiers Dia se présentent sous la forme d'un flux XML compressé en gzip (pas obligatoire).
      Un simple zcat donne le contenu suivant :
      [...]
            <dia:attribute name="text">
              <dia:composite type="text">
                <dia:attribute name="string">
                  <dia:string>#3. Récupéré
      et détruit#</dia:string>
                </dia:attribute>
                <dia:attribute name="font">
                  <dia:font family="sans" style="0" name="Helvetica"/>
                </dia:attribute>
                <dia:attribute name="height">
      [...]
      
      Je cherche à traduire les textes <dia:string>...</dia:string> et j'utilise pour cela un script sed qui ressemble à ça :
      [...]
      s/Espace noyau/Kernel-space/g
      s/Interface noyau/Kernel interface/g
      s/Vecteur de fonctions/Function vector/g
                                                                                      
      # Prog test
      N;s/envoie les\ninstructions/sends\ncommands/g
      N;s/envoie les\névénements/sends\nevents/g
      s/controle/controls/g
      [...]
      
      Apparement, le fait de rajouter les commandes 'N' décale le parsing réalisé par sed et je perd certains remplacements qui auraient du être fait. J'avoue que je ne suis pas à l'aise avec sed et ses manipulations de buffer... Une idée ?
      • [^] # Re: multi lignes

        Posté par  (site web personnel) . Évalué à 2.

        dans un premier temps, utiliser gawk qui te donnera des possibilités un peu plus grandes que sed

        ensuite, envisager de passer à perl (qui a des fonctions toutes faites mais qu'il faudra chercher)
        • [^] # Re: multi lignes

          Posté par  . Évalué à 2.

          Pour Perl, je maîtrise, ca ne posera pas de problème, mais je voulais éviter de mettre en branle un monstre comme Perl, juste pour effectuer une liste de substitutions sur un simple document texte.
          • [^] # Perl

            Posté par  . Évalué à 4.

            je voulais éviter de mettre en branle un monstre comme Perl

            Pour la machine, c'est effectivement plus lourd (mais si tu n'as pas un 486 avec 8 Mo de mémoire ou un matériel embarqué, ça ne devrait pas être bloquant), mais pour toi, pas nécessairement :
            perl -p0777e 's/toto\ntiti/tata/g' fichier

            Voire éventuellement plus intéressant, sauf si tu es sûr que toto et titi sont tout le temps séparés par une fin de ligne :
            perl -p0777e 's/toto\s+titi/tata/gs' fichier

            Ainsi,
            tyty toto titi tete
            tete tutu toto
            tititoto titi tyty

            devient :
            tyty tata tete
            tete tutu tatatata tyty

            Tiré de man perlrun :
            -p indique à Perl de considérer votre programme comme entouré par [une] boucle, qui le fait itérer sur les noms de fichiers passés en arguments, un peu à la manière de sed.
            -0[digits] indique le séparateur d'enregistrement en entrée ($/) en notation octale. [...] La valeur 0777, indique à Perl d'avaler les fichiers en entier car il n'y a pas de caractère avec cette valeur octale.

            Tiré de man perlop :
            s/MOTIF/REMPLACEMENT/egimosx
            s Traitement de la chaîne comme étant une seule ligne.
            (ainsi \s peut correspondre à \n).

            « Le fascisme c’est la gangrène, à Santiago comme à Paris. » — Renaud, Hexagone

            • [^] # Script façon sed en Perl

              Posté par  . Évalué à 3.

              Tu peux même te faire un script façon sed directement exécutable avec les options en question :

              #!/usr/bin/perl -p0777

              s/toto\ntiti/tata/gs;
              s/
              ...

              « Le fascisme c’est la gangrène, à Santiago comme à Paris. » — Renaud, Hexagone

              • [^] # Détail

                Posté par  . Évalué à 2.

                Je n'ai pas tout-à-fait bien repris mon exemple : l'intérêt de l'option s de la substitution est de pouvoir faire correspondre une fin de ligne à \s. C'est plus utile quand on utilise effectivement \s. Soit :

                #!/usr/bin/perl -p0777

                s/toto\s+titi/tata/gs;
                s/...

                « Le fascisme c’est la gangrène, à Santiago comme à Paris. » — Renaud, Hexagone

                • [^] # Re: Détail

                  Posté par  . Évalué à 1.

                  Bon, j'ai finalement opté pour cette solution.
                  Ca me résout pas mal de mes soucis, les règles sont beaucoup plus claires, que des avantages :)... Si ce n'est que je lance un process Perl pour chaque traitement.

                  Je connais très bien Perl, mais je me force justement à éviter de l'utiliser quand il existe d'autres solutions beaucoup plus légère, éviter d'utiliser un bulldozer pour écraser une mouche. C'est un bon réflexe je pense.

                  Mais bon, pour cette fois, Perl gère bien mieux les multi-lignes que 'sed', alors, profitons s'en ! :)

                  Merci pour votre aide.
                  • [^] # To Perl or not to Perl

                    Posté par  . Évalué à 2.

                    Si ce n'est que je lance un process Perl pour chaque traitement.

                    Alors pourquoi pas plutôt faire un script unique, plus conséquent, qui fasse tous les traitements ?

                    Je connais très bien Perl, mais je me force justement à éviter de l'utiliser quand il existe d'autres solutions beaucoup plus légère, éviter d'utiliser un bulldozer pour écraser une mouche.

                    Pour ma part, à l'inverse, c'est mon temps que j'essaie d'économiser plus que celui de la machine.
                    Aussi, je pars directement sur Perl. Comme ça, quand je m'aperçois qu'il est souhaitable de faire un traitement plus compliqué que prévu et que l'outil qui m'aurait semblé suffisant au départ ne l'est plus, eh bien je ne perds pas de temps à convertir ce que j'avais déjà fait en Perl, parce que ça y est depuis le départ.

                    C'est un bon réflexe je pense.

                    Je ne suis pas convaincu que ce choix ait dans l'informatique actuelle autant de portée qu'il en aurait eu auparavant.
                    Pour égaler avec Perl la charge infligée sur un poste de travail par un navigateur web soit-disant léger comme Firefox ou par OpenOffice, ou sur un serveur par un truc "hyper-efficace" comme OpenLDAP, il faut vraiment que le traitement soit très lourd, ou alors qu'on l'ait programmé comme un pied. La lourdeur due au temps de lancement ou à l'encombrement mémoire de Perl par rapport à bash, sed ou awk est alors négligeable...

                    Je dis ça par expérience personnelle.
                    Sur le PIII 450 (avec 512 Mo quand même) que j'ai encore comme poste de travail pour quelques semaines, les trucs les plus lourds que j'utilise sont Firefox, xpdf et OpenOffice; la machine serait encore assez puissante pour tout le reste. Je n'ouvre généralement pas documents Word que je reçois en attachement de mail : la plupart du temps, l'intérêt du contenu ne vaut pas le temps que met OpenOffice à se lancer.
                    J'utilise OpenLDAP sur un serveur et je faisais sur l'annuaire un traitement nécessitant un nombre conséquent de requêtes assez lourdes. J'en ai eu marre que ça me charge le serveur pendant plusieurs minutes. Maintenant, je charge directement tout l'annuaire avec un dump au niveau du backend (donc en court-circuitant OpenLDAP) et je fais tous les traitements en interne de mon script Perl. C'est carrément plus rapide. Et encore j'ai utilisé par facilité les structures fournies d'origine par Perl (tableau associatif notamment), plutôt que des structures plus optimales dans ce cas (par exemple un arbre pour stocker l'annuaire). Sans importance : mes traitements sont encore nettement moins longs que le chargement de l'annuaire, pourtant pas plus long qu'une seule grosse requête sur OpenLDAP.

                    « Le fascisme c’est la gangrène, à Santiago comme à Paris. » — Renaud, Hexagone

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.