Forum général.général [sed] Remplacer du texte contenu dans une variable et contenant des () et *

Posté par  (site Web personnel) . Licence CC By‑SA.
1
20
mai
2017

Edit du 22/05/17 à 12h30 : J'en suis là

Quand je fais :

sed -i -e "s/TRUC:(1000)\*80/TRUC:(1000)\*99/" fichier

Le fichier est correctement modifié. Mais avec le code suivant, ça ne marche pas, le fichier n'est pas modifié :

old_value="(1000)\*80"
new_value="(1000)\*99"
sed -i -e "s/TRUC:'$old_value'/TRUC:'$new_value'/" fichier

Post original :

J'ai plusieurs fichiers de config. à modifier comme suit :

config.js :

param_un:valeur_1,
param_deux:valeur_2,

Pas trop dur :

param_a_modifier="param_un"
ancienne_valeur="valeur_1"
nouvelle_valeur="valeur_3"
sed -i -e 's/${param_a_modifier}:${ancienne_valeur}/${param_a_modifier}:${nouvelle_valeur/' config.js

Jusque là tout va bien. Sauf que bien sûr, patatra :

ancienne_valeur="(1000)*30"
nouvelle_valeur="(1000)*300"

Je n'ai pas droit à un message d'erreur, mais sed ne fait rien. Le fichier n'est pas modifié. Je sèche.

  • # Regexp

    Posté par  (site Web personnel) . Évalué à 5.

    Pour sed "(1000)*30" signifie une expression rationnelle, valant "(100030" ou "(1000)30" ou "(1000)))))…)))))30". Il faut échapper * en \*.

    • [^] # Re: Regexp

      Posté par  (site Web personnel) . Évalué à 1. Dernière modification le 20/05/17 à 21:26.

      Oui, ok pour l'échapement, j'avais cherché de ce côté, merci.
      Le hic c'est que je peut pas échapper ce que je ne vois pas, en l’occurrence le contenu de la variable. Je ne peux que nommer la variable dans l'expression sed. Ou alors comment faire ?

      P.-S.: je viens de voir que c'est l'* qui pose souci. Je m'étais focalisé sur les (). Merci, je vais creuser de ce côté.

      « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

  • # Shell Parameter Expansion

    Posté par  . Évalué à 2.

    Si ça peut aider :

    $ echo $toto
    plop*plip
    $ echo ${toto//\*/\\*}
    plop\*plip
    

    Pas sûr que ça fonctionne dans ton cas cela dit.

    https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html

    • [^] # Re: Shell Parameter Expansion

      Posté par  (site Web personnel) . Évalué à 0.

      J'ai essayé avec la forme ${toto//\*/\\*} et aussi ${toto/\*/\\*/}, mais sed me renvoie :

      sed: -e expression n°1, caractère 35: option inconnue pour `s'

      Merci d'avoir essayé.

      « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

  • # .

    Posté par  . Évalué à 2. Dernière modification le 21/05/17 à 06:29.

    Il faut échapper les caractères spéciaux des expressions régulières basiques : '[', '*', '^' et '$' ainsi que le caractère d'échappement lui-même et le caractère qui sépare les arguments de la commande de substitution.

    Une solution utilisant Awk et qui ne requiert par d'échappements :

    awk -F: -vOFS=:\
        -vparam_a_modifier="$param_a_modifier" \
        -vancienne_valeur="$ancienne_valeur" \
        -vnouvelle_valeur="$nouvelle_valeur"  \
          '$1 == param_a_modifier && $2 == ancienne_valeur {$2 = nouvelle_valeur} 1'
    

    Remarquez le 1 à la fin de la commande qui assure que toutes les lignes seront imprimées.

    edit: ironiquement, j'ai du échapper le ^ dans le commentaire ^^
    edit2: ne pas oublier de déclarer ':' aussi comme séparateur de champs à la sortie (OFS)

    • [^] # Re: .

      Posté par  (site Web personnel) . Évalué à 1.

      Merci pour la solution avec awk. Je vais regarder ça de plus près (c'est dimanche) et je te tiens au jus.

      « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

      • [^] # Re: .

        Posté par  . Évalué à 1. Dernière modification le 21/05/17 à 13:59.

        Sinon pour échapper des caractères d'une variable :

        var=$(echo "$var" | sed 's,\.\|\[\|\*\|\^\|\$,\\&,g')
        Le '.' est aussi a échapper, ce que j'ai oubié de préciser dans mon dernier post

        • [^] # Re: .

          Posté par  . Évalué à 1. Dernière modification le 21/05/17 à 14:09.

          var=$(echo "$var" | sed 's,\.\|\[\|\*\|\^\|\$\|/\|\\,\\&,g')
          ```Voilà, j'avais oublié d'échapper le / et le \.
          
          • [^] # Re: .

            Posté par  (site Web personnel) . Évalué à 1.

            Quand je fais :

            sed -i -e "s/TRUC:(1000)\*80/TRUC:(1000)\*99/" fichier
            

            Le fichier est correctement modifié. Mais avec le code suivant, ça ne marche pas, le fichier n'est pas modifié :

            old_value="(1000)\*80"
            new_value="(1000)\*99"
            sed -i -e "s/BOT_SLEEP_DELAY:'$old_value'/BOT_SLEEP_DELAY:'$new_value'/" fichier
            

            Le lapin ne comprend pas !<

            « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

            • [^] # Re: .

              Posté par  . Évalué à 1.

              Les apostrophes sont de trop.

              • [^] # Re: .

                Posté par  (site Web personnel) . Évalué à 1.

                Même sans les apostrophes le résultat est identique. Le fichier n'est pas modifié. Merci.

                « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

                • [^] # Re: .

                  Posté par  . Évalué à 1.

                  Pourtant, en enlevant les apostrophes, ca fonctionne chez moi :

                  sed -i -e "s/BOT_SLEEP_DELAY:$old_value/BOT_SLEEP_DELAY:$new_value/" fichier
                  

                  ajoute un echo devant sed pour voir le résultat de l'expansion du script sed te paraît correct.

                  Attention, il faudrait échapper au moins le '&' dans le deuxième argument de s/// qui n'est pas une expression régulière mais une chaîne de substitution qui obéit à d'autres règles.

  • # pas la bonne chaine ?

    Posté par  . Évalué à 2.

    Edit du 22/05/17 à 12h30 : J'en suis là
    Quand je fais :

    sed -i -e "s/TRUC:(1000)\*80/TRUC:(1000)\*99/" fichier

    Le fichier est correctement modifié. Mais avec le code suivant, ça ne marche pas, le fichier n'est pas modifié :

        old_value="(1000)\*80"
        new_value="(1000)\*99"
        sed -i -e "s/BOT_SLEEP_DELAY:'$old_value'/BOT_SLEEP_DELAY:'$new_value'/" fichier

    parce que dans le premier cas tu cherches et remplaces TRUC:xxxxx par TRUC:yyyyy
    dans le 2e cas tu cherches BOT_SLEEP_DELAY:xxxx

    pourquoi ne pas avoir une fichier de configuration qui definit
    BOT_SLEEP_DELAY=80000 #l'equivalent de 1000 * 80

    ce fichier serait appelé par tous les autres fichiers du programme,
    et tu n'aurais alors qu'à modifier cette valeur à un seul endroit.

    • [^] # Re: pas la bonne chaine ?

      Posté par  (site Web personnel) . Évalué à 1.

      Le coup des deux chaînes différentes c'est bien sûr une erreur de copié-collé.
      Évidemment il serait plus simple de changer (1000)*80 par 80000. D'une je n'ai pas vraiment la main sur les fichiers de config., je ne peux que modifier les valeurs. Et deux, le problème se pose ailleurs. Enfin trois, j'aimerais comprendre pour la prochaine fois où ça m'arrivera.

      « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

  • # Tout simplement

    Posté par  (site Web personnel) . Évalué à 1.

    old_value="(1000)\*80"
    new_value="(1000)\*99"
    sed -i -e 's/BOT_SLEEP_DELAY:'"$old_value"'/BOT_SLEEP_DELAY:'"$new_value"'/' fichier
    
    • [^] # Re: Tout simplement

      Posté par  (site Web personnel) . Évalué à 1.

      Non plus :<

      « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

  • # et comme ca ?

    Posté par  . Évalué à 2.

    old_value="(1000)\\*80"
    new_value="(1000)\\*99"
    sed -i -e 's/BOT_SLEEP_DELAY:'"$old_value"'/BOT_SLEEP_DELAY:'"$new_value"'/' fichier

    parce que le shell va echapper le premier \* au moment de remplir old_value
    mais il faut l'echapper une 2e fois pour l'interpreter dans sed.

    si tu dois "debugger" des scripts bash, tu peux les lancer avec
    bash -x tonscript.sh

    ou mettre set -x avant le code à debugger et set +x apres (ou l'inverse je sais jamais

    [...]code avant[...]
    set -x
    old_value="(1000)\*80"
    new_value="(1000)\*99"
    sed -i -e 's/BOT_SLEEP_DELAY:'"$old_value"'/BOT_SLEEP_DELAY:'"$new_value"'/' fichier
    set +x
    [...]code apres[...]
    • [^] # Re: et comme ca ?

      Posté par  . Évalué à 3.

      oui enfin au lieu d'échapper les truc en cascade, on peut déjà limiter la casse en utilisant les simples ' lors de l'affectation de la variable…

      par ailleurs utiliser les [] pour échapper les caractères spéciaux à tendance à être beaucoup plus robuste, notamment dans les cas d'inclusion successive, il ne reste plus qu'à gérer le cas du ] s'il se présente :)

      old_value='[(]1000[)][*]80'
      new_value='[(]1000[)][*]99'
      ...

      Il ne faut pas décorner les boeufs avant d'avoir semé le vent

      • [^] # Re: et comme ca ?

        Posté par  (site Web personnel) . Évalué à 1.

        Merci. Le fichier contenant la ligne TRUC:(1000)*80 j'ai essayé :

        old_value='[(]1000[)][*]80'
        new_value='[(]1000[)][*]99'
        
        sed -e 's/TRUC:$old_value/TRUC:$new_value/' fichier
        #ou sed -e 's/TRUC:"$old_value"/TRUC:"$new_value"/' fichier
        #résultat de sed
        TRUC:(1000)*80,
        #pas de changement
        
        sed -e 's/TRUC:'$old_value'/TRUC:'$new_value'/' fichier
        #resultat de sed
        TRUC:[(]1000[)][*]99
        

        Là ça marchouille, il faudrait que je repasse la moulinette pour supprimer les []. Je vais creuser de ce côté aussi. Pour l'instant je regarde si je ne peux pas faire tout ça avec awk.
        Merci.

        « Il vaut mieux mobiliser son intelligence sur des conneries que mobiliser sa connerie sur des choses intelligentes. »

        • [^] # Re: et comme ca ?

          Posté par  . Évalué à 2.

          pour la new value y a juste pas besoin de protéger les caractères spéciaux par des [] :), c'est juste pour la old_value.

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

  • # Brute force

    Posté par  . Évalué à 1. Dernière modification le 23/05/17 à 00:33.

    ancienne_valeur="`echo "$ancienne_valeur" | sed 's/./[&]/g'"
    nouvelle_valeur="` idem  | sed 's/&/\\&/g'`"
    

    À priori cela devrait marcher dans la plupart si pas tous les cas (à tester…), l'idée est d'échapper systématiquement tous les caractères par un singleton, ce qui empêche leur interprétation en tant que méta-caractère.

Suivre le flux des commentaires

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