Précédent   Forum des professionnels en informatique > Systèmes > Linux > Applications > Shell
Shell Vos questions sur l'utilisation des commandes shell
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
Vieux 17/03/2010, 12h33   #1
Invité régulier
 
Inscription : septembre 2009
Messages : 12
Détails du profil
Informations forums :
Inscription : septembre 2009
Messages : 12
Points : 6
Points : 6
Par défaut Remplacer un motif avec le contenu d'une variable via sed / awk

Bonjour à tous.

J'ai un problème plus que tordu...

Voilà, j'ai un fichier fichier1 dont le contenu peut ressembler à ca :


Code :
1
2
3
4
 
truc __var__ chose
 
      machin __var2__ bidule


J'ai une variable var qui vaut "valeur1" et une variable var2 qui vaut "valeur2"


Code :
1
2
var1=valeur1
var2=valeur2

L'idée, c'est donc de copier mon fichier1 vers fichier2 et de remplacer dans fichier2 __var__ par le contenu de $var et __var2__ par le contenu de $var2...


Pour l'instant, j'arrive à faire ca :


Code :
cat fichier1 | sed 's/__\(.*\)__/${\1}/g' >fichier2

Ce qui donne un fichier2 qui ressemble à :


Code :
1
2
3
4
 
truc ${var1} chose
 
      machin ${var2} bidule


Le résultat attendu serait plutôt :

Code :
1
2
3
4
 
truc valeur1 chose
 
      machin valeur2 bidule

A préciser bien sûr que si j'ai, dans fichier1, le motif __supervariable__, il doit être remplacé par le contenu de $supervariable...


La substitution des variables fonctionne avec le code suivant :


Code :
1
2
3
while read i; do
echo $i | eval echo $(sed 's/__\(.*\)__/${\1}/g') >>fichier2
done <fichier1

Le souci, c'est que mon fichier2 s'en retrouve amputé de toutes les indentations... J'imagine que c'est le eval qui est responsable de cette amputation...

Et du coup, là, je bloque... J'ai essayé avec awk et du gensub, mais je suis confronté aux mêmes problèmes finalement...


Si quelqu'un a une idée, c'est avec grand plaisir que j'accueillerais sa solution


Merci d'avance à tous.


Deupac.
deupac est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 12h46   #2
Expert Confirmé
 
Avatar de N_BaH
 
Inscription : février 2008
Messages : 1 897
Détails du profil
Informations forums :
Inscription : février 2008
Messages : 1 897
Points : 3 677
Points : 3 677
Bonjour deupac,

Je ne comprend pas...
tu connais la valeur de var1 du fichier_1 ?

dans le pire des cas :
Code :
sed 's/ancien/'"$nouvau"'/g' fichier
...
?
N_BaH est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 13h36   #3
Membre chevronné
 
Inscription : septembre 2007
Messages : 685
Détails du profil
Informations personnelles :
Âge : 48
Localisation : Suisse

Informations forums :
Inscription : septembre 2007
Messages : 685
Points : 723
Points : 723
une réponse avec awk:
Code :
1
2
3
4
5
6
awk -v v1=$var1 -v v2=$var2 '
{
  gsub("__var__", v1)
  gsub("__var2__", v2)
  print
}' fichier_1 > fichier_2
L'explication du problème est un peu longue, mais elle est précise; elle permet d'aller rapidement vers une solution.
__________________
Un problème bien posé est déjà résolu (H. Bergson).
jmelyn est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 14h01   #4
Invité régulier
 
Inscription : septembre 2009
Messages : 12
Détails du profil
Informations forums :
Inscription : septembre 2009
Messages : 12
Points : 6
Points : 6
Merci pour la solution, elle fonctionne.

Le problème est que je ne connais pas forcément la totalité des motifs que contient fichier1.


Pour être plus précis, je peux tout à fait avoir une collection de variables :

Code :
1
2
3
4
var1=valeur1
var2=valeur2
var3=valeur3
var4=valeur4

Le fichier1 peut quant à lui contenir un, plusieurs ou aucun des motifs correspondant. Par exemple on peut avoir un fichier1 qui ressemble à ca :


Code :
1
2
bonjour __var__
        coucou __var4__

ou bien qui ressemble à :


ou encore à :

Code :
hello __var__ et __var3__

Le nombre de motifs n'est pas connu d'avance, et donc remplacer spécifiquement un motif par sa variable correspondante n'est pas possible.


Merci !
deupac est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 14h28   #5
Membre chevronné
 
Inscription : septembre 2007
Messages : 685
Détails du profil
Informations personnelles :
Âge : 48
Localisation : Suisse

Informations forums :
Inscription : septembre 2007
Messages : 685
Points : 723
Points : 723
En (très) rapide:

Si tu as les variables var_1 à var_n dans ton script, c'est que tu connais le nombre max de changement à faire: n. Il faut alors demander les n changements dans le fichier à traiter. Si le motif n'est pas présent, c'est pas grave: aucun changement ne sera effectué.
Code :
1
2
3
4
5
6
7
8
awk -v v_1=$var_1 -v v_2=$var_2 ... -v v_n=$var_n '
{
  gsub("__var__", v_1)
  gsub("__var2__", v_2)
  ...
  gsub("__varn__", v_n)
  print
}' fichier_1 > fichier_2
__________________
Un problème bien posé est déjà résolu (H. Bergson).
jmelyn est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 14h39   #6
Invité régulier
 
Inscription : septembre 2009
Messages : 12
Détails du profil
Informations forums :
Inscription : septembre 2009
Messages : 12
Points : 6
Points : 6
Je suis bien d'accord avec toi et j'avais déjà pensé à cela, mais dans un souci d'évolutivité et de maintenabilité, si je rajoute une variable à ma collection de base, je préfèrerais ne pas avoir à l'ajouter en plus dans le awk, d'où mon besoin de substitution des variables de façon dynamique (je sais pas si je suis clair dans mes explications, si je ne le suis pas, il ne faut pas hésiter à me le signaler )
deupac est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 14h55   #7
Expert Confirmé
 
Avatar de N_BaH
 
Inscription : février 2008
Messages : 1 897
Détails du profil
Informations forums :
Inscription : février 2008
Messages : 1 897
Points : 3 677
Points : 3 677
Par défaut HS: langue

"maintenabilité" ? oh, le barbarisme !
ça existe ?

entretien (à venir)
...
?
N_BaH est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 15h24   #8
Invité régulier
 
Inscription : septembre 2009
Messages : 12
Détails du profil
Informations forums :
Inscription : septembre 2009
Messages : 12
Points : 6
Points : 6
http://fr.wikipedia.org/wiki/Maintenabilit%C3%A9

Néologisme à la limite si tu veux, barbarisme, on va peut-être pas exagérer.

Merci néanmoins pour ta constructive contribution.
deupac est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 15h48   #9
Expert Confirmé
 
Avatar de N_BaH
 
Inscription : février 2008
Messages : 1 897
Détails du profil
Informations forums :
Inscription : février 2008
Messages : 1 897
Points : 3 677
Points : 3 677
Par défaut HS : langue

soit, c'est un terme entendu/convenu.
(argh, je deteste les conventions, quand elles ne sont pas essentielles )

reste qu'il est horrible !
N_BaH est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/03/2010, 16h12   #10
Invité régulier
 
Inscription : septembre 2009
Messages : 12
Détails du profil
Informations forums :
Inscription : septembre 2009
Messages : 12
Points : 6
Points : 6
Reste qu'un terme qu'on trouve aussi dans le dictionnaire larousse ne peut selon moi plus vraiment être appelé "entendu / convenu". En fait ca existe, point barre.

Par contre, puisque je n'avais pas voulu lancer un débat de langue vivante dans mon premier post, j'en reviens à l'essentiel...

Bref, voilà une solution à mon problème pour ceux que cela pourrait intéresser...
Je reprends depuis le début. Soit un fichier1 :


Code :
1
2
3
4
5
bonjour __var__ coucou __var2__ et __var3__
 
        coucou __var2__
 
salut __var3__

On se moque donc de savoir si les variables $var, $var2, $var3 etc sont valorisées..., si elles sont vides, alors les motifs correspondants seront remplacés par du vide.


Le code :

Code :
1
2
3
while read line; do
echo "$line" | eval echo $(awk '{for (NUM_CHAMP = 1; NUM_CHAMP <= NF ; NUM_CHAMP++) {if ($NUM_CHAMP ~ /^__/ && $NUM_CHAMP ~ /__$/) ; { gsub(/^__/,"${",$NUM_CHAMP) ; gsub(/__$/,"}",$NUM_CHAMP) }  }  print }')
done < fichier1


Le résultat (en admettant que $var, $var2 et $var3 soient respectivement valorisées à valeur, valeur2 et valeur3) :


Code :
1
2
3
4
5
bonjour valeur coucou valeur2 et valeur3
 
coucou valeur2
 
salut valeur3


Autre résultat, en admettant que seules $var et $var2 soient valorisées :


Code :
1
2
3
4
5
bonjour valeur coucou valeur2 et
 
coucou valeur2
 
salut

Les occurrences de __var3__ sont remplacées par du vide.


Le seul truc qui ne correspond pas à ce que je veux, c'est que je perd les tabulations. Je n'ai pas encore trouvé de moyen de les conserver, mais je garde espoir.


Merci pour vos contributions.

Deupac.
deupac est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 18/03/2010, 00h50   #11
Invité régulier
 
Inscription : septembre 2009
Messages : 12
Détails du profil
Informations forums :
Inscription : septembre 2009
Messages : 12
Points : 6
Points : 6
Les réponses ne se bousculent pas, aussi j'imagine que le post n'intéresse pas grand monde, cependant, je vais quand même poster la solution que j'ai adopté (qui a vachement moins la classe qu'une paire de sed et de awk, mais tant pis )

Le awk que je proposais juste avant me posait un problème : si le motif __*__ n'était pas suivi d'un caractère vide, ca ne fonctionnait pas.


Soit le fichier "fichier" :

Code :
1
2
3
bonjour __var_a_la_con__ coucou __var2__ et __var3__ et surtout __var__/__var2__
 
        hello __var__ hello


Le script complet qui fait la transformation est comme ca :


Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
 
# en vrai ces variables seront pas déclarées ici mais c'est pour l'exemple
var=valeur1
var2=valeur2
var3=valeur3
var_a_la_con=valeur_toute_moisie
 
rm -f autrefichier &>/dev/null
 
cp fichier autrefichier
 
for i in $(grep -o __[A-Za-z0-9_]*__ fichier | sed 's/__\([A-Za-z0-9_]*\)__/\1/g')
   do
   lavar=$i
   sed -i 's/__'"$i"'__/'"${!lavar}"'/g' autrefichier    
done



Explications :

le cp du fichier source vers le fichier cible permet de garder l'indentation si chère à mes yeux.

dans le for : le grep -o récupère et n'affiche que les motifs __*__
dans le for : le sed dégage les __ du résultat renvoyé par le grep -o

lavar stocke le nom du motif courant amputé de ses __

le sed : remplace le motif complet courant par le contenu de la variable qui porte le même nom que le motif sans __, ceci via référence indirecte, puisque si var=coucou et que coucou=bonjour, alors ${!var} = bonjour. le sed modifie bien entendu directement le fichier cible.


Le fichier "autrefichier" resultat :

Code :
1
2
3
4
bonjour valeur_toute_moisie coucou valeur2 et valeur3 et surtout valeur1/valeur2

        hello valeur1 hello

J'en profite pour passer le post en résolu. Merci à jmelyn, seul et unique contributeur à mon problème


Deupac.
deupac est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 18/03/2010, 14h14   #12
Membre chevronné
 
Inscription : septembre 2007
Messages : 685
Détails du profil
Informations personnelles :
Âge : 48
Localisation : Suisse

Informations forums :
Inscription : septembre 2007
Messages : 685
Points : 723
Points : 723
Bonjour Deupac,

Tout d'abord je voudrais préciser ceci: ce n'est pas parce que je ne mets pas de réponse que je ne réfléchis pas au problème. Hier j'ai passé un moment sans solution correcte. Et puis au travail ce n'est pas ma première priorité, même si je m'améliore très nettement en lisant les problèmes, en cherchant des solutions et en découvrant les solutions des autres.

Cet aprèm j'ai trouvé quelque chose qui pourrait t'intéresser:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
 
unset var
var[1]=valeur_1
var[2]=valeur_2
var[3]=valeur_3
...
 
awk -v list="${var[*]}" '
BEGIN {size=split(list, v)}
{
  gsub("__var__", v[1])
  for (i=2; i <= size; i++)
  {
    gsub("__var" i "__", v[i])
  }
  print
}' file_in > file_out
Attention, cette solution ne fonctionne que si les valeurs valeur_1, valeur_2..., valeur_n ne contiennent pas d'espace.
__________________
Un problème bien posé est déjà résolu (H. Bergson).
jmelyn est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 18/03/2010, 20h30   #13
Membre chevronné
 
Inscription : septembre 2007
Messages : 685
Détails du profil
Informations personnelles :
Âge : 48
Localisation : Suisse

Informations forums :
Inscription : septembre 2007
Messages : 685
Points : 723
Points : 723
Alors, petite restriction précédente enlevée: il peut maintenant y avoir des espaces dans les variables var[i]:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

unset var
var[1]="valeur 1"
var[2]="valeur 2"
var[3]="valeur 3"
...
sep="#"
IFS=$sep
awk -v list="${var[*]}" -v s="$sep" '
BEGIN {size=split(list, v, s)}
{
  gsub("__var__", v[1])
  for (i=2; i <= size; i++)
  {
    gsub("__var" i "__", v[i])
  }
  print
}' file_in > file_out
Les modifications par rapport à mon précédent post son en bleu. Le truc ici est la valeur "${var[*]}": c'est la liste des valeurs du tableau var séparées par le premier caractère de IFS, parce que j'ai utilisé les doubles-quotes. Fallait le savoir!

Bien sûr, je suppose que le caractère '#' n'apparait pas dans les valeurs de var puisque c'est un séparateur. Mais c'est maintenant paramétrable, on peut mettre ce qu'on veut.
__________________
Un problème bien posé est déjà résolu (H. Bergson).
jmelyn est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +1. Il est actuellement 02h14.


 
 
 
 
Partenaires

Hébergement Web