Utilisation de sed pour trouver et remplacer une chaîne complexe (de préférence par regex)

Jai un fichier avec le contenu suivant:

<username><![CDATA[name]]></username> <password><![CDATA[password]]></password> <dbname><![CDATA[name]]></dbname> 

et je dois faire un script qui change le » nom « de la première ligne en » quelque chose « , le » mot de passe « sur la deuxième ligne en » quelque chose dautre « et le » nom « dans la troisième ligne à « quelque chose de différent ». Je ne peux pas me fier à lordre de ces événements dans le fichier, donc je ne peux 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». En fait, jai besoin de rechercher les chaînes environnantes pour massurer que je trouve et remplace la bonne chose.

Jusquà présent, jai 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 caractères pourraient avoir besoin dêtre échappés, etc.

Idéalement, je » Jadore pouvoir utiliser regex pour faire correspondre les deux occurrences de « nom dutilisateur » 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?

Commentaires

  • Notez simplement que quasiment toutes les solutions basées sur les expressions rationnelles, à moins quelles ne soient extrêmement artificielles, risquent rupture à chaque fois que le format dentrée change. Les expressions régulières sont un mauvais choix pour traiter du XML, du SGML ou des dérivés (ce que cela me semble).
  • Approuvé! Pensez à utiliser XQuery par exemple: w3schools.com/xquery/default.asp . Il sagit de la norme W3C pour la récupération et la manipulation de contenu XML.

Réponse

sed -i -E "s/(<username>.+)name(.+<\/username>)/\1something\2/" file.xml 

Cest, je pense, ce que vous recherchez.

Explication:

  • les parenthèses dans la première partie définissent des groupes (des chaînes en fait) qui peuvent être réutilisés dans la deuxième partie
  • \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écessaire pour + et regroupement).

Commentaires

  • +1 pour loption -E
  • it laisse un fichier de sauvegarde, avec le nom (original name) + "-E".
  • Sous OSX, jobtiens ‘ sed: 1:  » s / (< nom dutilisateur >. +) nom (. + … « : \ 1 pas défini dans le RE ‘. Jai collé lexemple exact de cette question dans un fichier. puis jai exécuté la commande de cette réponse sur ce fichier. Peut-être quOSX a une syntaxe différente?
  • La version gnu de sed prend en charge le paramètre  » -E « , mais pas officiel. Il ‘ nest même pas mentionné dans la page de manuel. Si vous souhaitez utiliser lexpression régulière étendue, vous devez utiliser le paramètre  » -r  » à la place.
  • @ deweydb Selon cette réponse , vous devez utiliser \( et \) au lieu de ( et ).

Réponse

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 pour ne travailler que sur les lignes contenant la chaîne « username ».

Commentaires

  • Élégant, efficace et parfaitement adapté au cas. +1

Réponse

Si sed nest pas difficile exigence, mieux vaut utiliser un outil dédié à la place.

Si votre fichier est du XML valide (pas seulement ces 3 balises de type XML), 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:

  • Peut remplacer les valeurs des balises sans spécifier leurs valeurs actuelles.
  • Peut remplacer les valeurs même si elles sont simplement échappées et non incluses dans CDATA.
  • Peut remplacer les valeurs même si les balises ont des attributs.
  • Peut facilement remplacer uniquement les occurrences de balises, sil y en a plusieurs avec le même nom.
  • Peut formater le XML modifié en lindentant.

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> 

Réponse

$ sed -e "1s/name/something/2" \ -e "3s/name/somethingdifferent/2" \ -e "s/password/somethingelse/2" sample.xml 

Vous pouvez simplement utiliser les adresses a s dans le nombre 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 correspondre.

Réponse

Vous devez citer \[.*^$/ dans la partie expression régulière du s commande et \&/ dans la pièce de rechange, plus les retours à la ligne. Lexpression régulière est une expression régulière de base , et vous devez en outre citer le délimiteur de la commande s.

Vous pouvez choisir un autre délimiteur pour éviter davoir à citer /. Vous devrez plutôt citer ce caractère, mais en général, le point de changer le délimiteur est den choisir un qui napparaît pas dans le texte à remplacer ou 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 du texte de remplacement, et prendre en compte les variations sur 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~" 

Réponse

Pour remplacer le mot « name » par le mot « something », 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.

Réponse

Usage: sed [OPTION]... {script-only-if-no-other-script} [input-file]... -r, --regexp-extended use extended regular expressions in the script. 

afin de remplacer la valeur dans un fichier de propriétés

sed -i -r "s/MAIL\=(.+)/MAIL\[email protected]/" etc/service.properties 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *