Usando sed para buscar y reemplazar cadenas complejas (preferiblemente con expresiones regulares)

Tengo un archivo con el siguiente contenido:

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

y necesito crear un script que cambie el» nombre «en la primera línea a» algo «, la» contraseña «en la segunda línea a» algo «y el» nombre «en la tercera línea a «algo diferente». No puedo confiar en el orden en que aparecen en el archivo, por lo que no puedo simplemente reemplazar la primera aparición de «nombre» con «algo» y la segunda aparición de «nombre» con «algo diferente». De hecho, necesito hacer una búsqueda de las cadenas circundantes para asegurarme de que estoy encontrando y reemplazando lo correcto.

Hasta ahora he probado este comando para encontrar y reemplazar la primera aparición del «nombre»:

sed -i "s/<username><![CDATA[name]]><\/username>/something/g" file.xml 

Sin embargo, no funciona, así que creo que algunos de estos personajes podrían necesitar escapar, etc.

Idealmente, » Me encantaría poder usar expresiones regulares para hacer coincidir las dos apariciones de «nombre de usuario» y reemplazar solo el «nombre». Algo como esto pero con sed:

<username>.+?(name).+?</username> 

y reemplace el contenido entre paréntesis con «algo».

¿Es esto posible?

Comentarios

  • Solo tenga en cuenta que prácticamente cualquier solución basada en expresiones regulares, a menos que sea extremadamente artificial, correrá el riesgo de rompiendo cada vez que cambia el formato de entrada. Las expresiones regulares son una mala elección para tratar con XML, SGML o derivados (que esto me parece).
  • ¡Aprobado! Considere usar XQuery, por ejemplo: w3schools.com/xquery/default.asp . Este es el estándar W3C para recuperar y manipular contenido XML.

Respuesta

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

Esto es, creo, lo que estás buscando.

Explicación:

  • Los paréntesis en la primera parte definen grupos (cadenas de hecho) que se puede reutilizar en la segunda parte
  • \1, \2, etc. En la segunda parte hay referencias al i-ésimo grupo capturado en la primera parte (la numeración comienza con 1)
  • -E habilita expresiones regulares extendidas (necesarias para + y agrupación).

Comentarios

  • +1 para la opción -E
  • it deja un archivo de respaldo, con el nombre (original name) + "-E".
  • En OSX obtengo ‘ sed: 1: » s / (< nombre de usuario >. +) nombre (. + … «: \ 1 no definido en el RE ‘. Pegué el ejemplo exacto de esta pregunta en un archivo. luego ejecuté el comando de esta respuesta en ese archivo. ¿Quizás OSX tiene una sintaxis diferente?
  • La versión gnu de sed admite el parámetro » -E «, pero no oficial. ‘ ni siquiera se menciona en la página de manual. Si desea usar la expresión regular extendida, debe usar el parámetro » -r «.
  • @ deweydb De acuerdo con esta respuesta , debes usar \( y \) en lugar de ( y ).

Responder

sed -e "/username/s/CDATA\[name\]/CDATA\[something\]/" \ -e "/password/s/CDATA\[password\]/CDATA\[somethingelse\]/" \ -e "/dbname/s/CDATA\[name\]/CDATA\[somethingdifferent\]/" file.txt 

El /username/ antes del s le dice a sed para trabajar solo en líneas que contengan la cadena «username».

Comentarios

  • Elegante, eficiente y perfectamente adaptado al caso. +1

Responder

Si sed no es difícil requisito, mejor use una herramienta dedicada en su lugar.

Si su archivo es XML válido (no solo esas 3 etiquetas de aspecto XML), entonces puede usar XMLStarlet :

xml ed -P -O -L \ -u "//username/text()" -v "something" \ -u "//password/text()" -v "somethingelse" \ -u "//dbname/text()" -v "somethingdifferent" file.xml 

Lo anterior también funcionará en situaciones que serían difíciles de resolver con expresiones regulares:

  • Puede reemplazar los valores de las etiquetas sin especificar sus valores actuales.
  • Puede reemplazar los valores incluso si solo son de escape y no están incluidos en CDATA.
  • Puede reemplazar los valores incluso si las etiquetas tienen atributos.
  • Puede reemplazar fácilmente solo las ocurrencias de etiquetas, si hay varias con el mismo nombre.
  • Puede formatear el XML modificado con sangría.

Breve demostración de lo anterior:

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> 

Respuesta

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

Simplemente puede usar direcciones s en el número que precede a la «s» que indica el número de línea.

Además, el número al final le dice a sed que reemplace la segunda coincidencia en lugar de reemplazar la primera partido.

Respuesta

Debe citar \[.*^$/ en la parte de expresión regular del s comando y \&/ en la pieza de repuesto, más nuevas líneas. La expresión regular es una expresión regular básica y, además, debe citar el delimitador del comando s.

Puede elegir un delimitador diferente para evitar tener que citar /. Tendrá que citar ese carácter en su lugar, pero por lo general el objetivo de cambiar el delimitador es elegir uno que no aparezca en el texto para reemplazar o en el texto de reemplazo.

sed -e "s~<username><!\[CDATA\[name\]\]></username>~<username><![CDATA[something]]></username>~" 

Puede usar grupos para evitar la repetición de algunas partes en el texto de reemplazo y acomodar variaciones en estas partes.

sed -e "s~\(<username><!\[[A-Z]*\[\)name\(\]\]></username>\)~\1something\2~" sed -e "s~\(<username>.*[^A-Za-z]\[\)name\([^A-Za-z].*</username>\)~\1something\2~" 

Respuesta

Para reemplazar la palabra «nombre» con la palabra «algo», use:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml 

Eso reemplazará todas las apariciones de la palabra especificada.

Hasta ahora, todo se envía a la salida estándar, puede usar:

sed "s/\(<username><\!\[[A-Z]*\[\)name\]/\1something/g" file.xml > anotherfile.xml 

para guardar los cambios en otro archivo.

Responder

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

para reemplazar el valor en un archivo de propiedades

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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *