J'ai un fichier avec le contenu suivant:
<username><![CDATA[name]]></username>
<password><![CDATA[password]]></password>
<dbname><![CDATA[name]]></dbname>
et je dois créer un script qui change le "nom" dans la première ligne en "quelque chose", le "mot de passe" sur la deuxième ligne en "quelque chose d'autre" et le "nom" dans la troisième ligne en "quelque chose de différent". Je ne peux pas me fier à leur ordre d'apparition dans le fichier, je ne peux donc pas simplement remplacer la première occurrence de "nom" par "quelque chose" et la deuxième occurrence de "nom" par "quelque chose de différent". J'ai en fait besoin de rechercher les chaînes environnantes pour m'assurer que je trouve et remplace la bonne chose.
Jusqu'à présent, j'ai essayé cette commande pour trouver et remplacer la première occurrence de "nom":
sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml
mais cela ne fonctionne pas, donc je pense que certains de ces personnages pourraient avoir besoin de s'échapper, etc.
Idéalement, j'aimerais pouvoir utiliser l'expression régulière pour faire correspondre les deux occurrences de "nom d'utilisateur" et remplacer uniquement le "nom". Quelque chose comme ça mais avec sed
:
<username>.+?(name).+?</username>
et remplacez le contenu entre crochets par "quelque chose".
Est-ce possible?
sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml
C'est, je pense, ce que vous cherchez.
Explication:
\1
, \2
, etc. dans la deuxième partie sont des références au i-ème groupe capturé dans la première partie (la numérotation commence par 1)-E
active les expressions régulières étendues (nécessaires pour +
et regroupement).sed -e '/username/s/CDATA\[name\]/CDATA\[something\]/' \
-e '/password/s/CDATA\[password\]/CDATA\[somethingelse\]/' \
-e '/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/' file.txt
Le /username/
avant le s
indique à sed de ne travailler que sur les lignes contenant la chaîne 'username'.
Si sed
n'est pas une exigence difficile, utilisez plutôt un outil dédié à la place.
Si votre fichier est du XML valide (pas seulement ces 3 balises XML), alors vous pouvez utiliser XMLStarlet :
xml ed -P -O -L \
-u '//username/text()' -v 'something' \
-u '//password/text()' -v 'somethingelse' \
-u '//dbname/text()' -v 'somethingdifferent' file.xml
Ce qui précède fonctionnera également dans des situations qui seraient difficiles à résoudre avec des expressions régulières:
Brève démonstration de ce qui précède:
bash-4.2$ cat file.xml
<sith>
<master>
<username><![CDATA[name]]></username>
</master>
<apprentice>
<username><![CDATA[name]]></username>
<password>password</password>
<dbname foo="bar"><![CDATA[name]]></dbname>
</apprentice>
</sith>
bash-4.2$ xml ed -O -u '//apprentice/username/text()' -v 'something' -u '//password/text()' -v 'somethingelse' -u '//dbname/text()' -v 'somethingdifferent' file.xml
<sith>
<master>
<username><![CDATA[name]]></username>
</master>
<apprentice>
<username><![CDATA[something]]></username>
<password>somethingelse</password>
<dbname foo="bar"><![CDATA[somethingdifferent]]></dbname>
</apprentice>
</sith>
$ sed -e '1s/name/something/2' \
-e '3s/name/somethingdifferent/2' \
-e 's/password/somethingelse/2' sample.xml
Vous pouvez simplement utiliser des adresses comme dans le numéro précédant "s" qui indique le numéro de ligne.
Le nombre à la fin indique également à sed
de remplacer la deuxième correspondance au lieu de remplacer la première correspondance.
Vous devez citer \[.*^$/
dans la partie expression régulière de la commande s
et \&/
dans la pièce de rechange, plus les nouvelles lignes. L'expression régulière est une expression régulière de base , et en plus vous devez citer le délimiteur pour la commande s
.
Vous pouvez choisir un délimiteur différent pour éviter d'avoir à citer /
. Vous devrez citer ce caractère à la place, mais généralement le point de changer le délimiteur est d'en choisir un qui n'apparaît ni dans le texte à remplacer ni dans le texte de remplacement.
sed -e 's~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~'
Vous pouvez utiliser des groupes pour éviter de répéter certaines parties dans le texte de remplacement et tenir compte des variations de ces parties.
sed -e 's~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~'
sed -e 's~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~'
Pour remplacer le mot "nom" par le mot "quelque chose", utilisez:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml
Cela va remplacer toutes les occurrences du mot spécifié.
Jusqu'à présent, tout est sorti en sortie standard, vous pouvez utiliser:
sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml
pour enregistrer les modifications dans un autre fichier.
Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]...
-r, --regexp-extended
use extended regular expressions in the script.
donc pour remplacer la valeur dans un fichier de propriétés
sed -i -r 's/MAIL\=(.+)/MAIL\[email protected]/' etc/service.properties