2010-04-23 34 views
6

Necesito escribir un script simple para reemplazar un bloque de texto en un archivo de configuración con el contenido de otro archivo.Reemplazar el bloque delimitado de texto en el archivo con el contenido de otro archivo

Asumamos con tener los siguientes archivos simplificadas:

server.xml

<?xml version='1.0' encoding='UTF-8'?> 
<Server port="8005" shutdown="SHUTDOWN"> 
    <Service name="Catalina"> 
    <Connector port="80" protocol="HTTP/1.1"/> 
    <Engine name="Catalina" defaultHost="localhost"> 
     <!-- BEGIN realm --> 
     <sometags/> 
     <sometags/> 
     <!-- END realm --> 
     <Host name="localhost" appBase="webapps"/> 
    </Engine> 
    </Service> 
</Server> 

realm.xml

<Realm className="org.apache.catalina.realm.UserDatabaseRealm" 
     resourceName="UserDatabase"/> 

Quiero ejecutar una secuencia de comandos y tienen realm.xml reemplazar el contenido entre el <!-- BEGIN realm --> y <!-- END realm --> líneas. Si realm.xml cambia, cada vez que se ejecuta el script nuevamente, reemplazará las líneas nuevamente con el nuevo contenido de realm.xml. Está previsto que se ejecute en /etc/init.d/tomcat al iniciar el servicio en varias instalaciones en las que el reino será diferente.

No estoy tan seguro de cómo puedo hacer esto simplemente con awk o sed.

Respuesta

12

seguirlo:

sed -i -ne '/<!-- BEGIN realm -->/ {p; r realm.xml' -e ':a; n; /<!-- END realm -->/ {p; b}; ba}; p' server.xml 
+0

Whoa ... funciona. Estoy intentando dominar la ramificación para entender realmente lo que está pasando. – rmarimon

+3

Las ramas 'ba' para etiquetar" a "dentro de las llaves asociadas con la prueba para" BEGIN "y las ramas' b' hasta el final cuando se encuentra "END", ya que está en un conjunto de llaves asociadas con esa prueba. Es algo así como 'if/BEGIN/then read file; while not/END/do skip line'. –

+0

Obtengo un error de sintaxis con esto: 'sed: -e expresión # 1, char 39: inesperado'} '' –

3
TOTAL_LINES=`cat server.xml | wc -l` 
BEGIN_LINE=`grep -n -e '<!-- BEGIN realm -->' server.xml | cut -d : -f 1` 
END_LINE=`grep -n -e '<!-- END realm -->' server.xml | cut -d : -f 1` 
TAIL_LINES=$(($TOTAL_LINES-$END_LINE)) 

head -n $BEGIN_LINE server.xml > server2.xml 
cat realm.xml > server2.xml 
tail -n $TAIL_LINES server.xml > server2.xml 

(OK, esto no utiliza awk o sed ... Supuse que no era un requisito excluyente :-)

+0

No era un requisito exclusivo ;-) – rmarimon

+0

¿Funciona? TOTAL_LINES tendrá un valor que incluya la cadena "server.xml" en la mayoría de las versiones de wc, por lo que sospecho que la aritmética fallará. –

+0

@William Pursell - buen punto, arreglado. –

2

puede usar awk

awk 'FNR==NR{ _[++d]=$0;next} 
/BEGIN realm/{ 
    print 
    for(i=1;i<=d;i++){ print _[i] } 
    f=1;next 
} 
/END realm/{f=0}!f' realm.xml server.xml > temp && mv temp server.xml 

realm.xml es pasó a awk como el primer archivo. FNR == NR significa obtener los registros del primer archivo pasado y almacenar en la variable _. awk procesará el siguiente archivo una vez FNR! = NR. si awk encuentra /BEGIN realm/, imprima la línea BEGIN realm, luego imprima lo que está almacenado en _. Al establecer una bandera (f) en 1, el resto de las líneas después de BEGIN realm no se imprimirá hasta que se detecte /END realm/.

+0

Este parece ser el enfoque correcto, pero es muy críptico. ¿Podría darnos algunas pistas sobre cómo funciona esto? – rmarimon

+0

¿Cómo cambiaría esto para que pueda reemplazarlo in situ como "sed-i"? – rmarimon

+0

solo necesita redirigir al archivo temporal y cambiarle el nombre. – ghostdog74

1

¿Qué hay de este pequeño fragmento que he creado:

sed -n \ 
    -e "1,/<\!-- BEGIN realm -->/ p" \ 
    -e"/<\!-- END realm -->/,$ p" \ 
    -e "/<\!-- BEGIN realm -->/ r realm.xml" \ 
    server.xml 

Los primeros comandos imprime las líneas hasta <!- BEGIN realm --> el segundo comando imprime la línea que comienza en <!-- END realm --> y la tercera comandos añadir el texto en el reino de archivos'. xml '. Si solo pudiera simplificar la eliminación de las líneas entre <!- BEGIN realm --> y <!-- END realm --> sin quitar las líneas del marcador, sería lo más simple posible. Y se puede hacer inplace con sed !!!

+0

¿qué tal ''? su comando sed no reemplaza ''. – ghostdog74

+1

Cuando lo ejecuto en mi máquina Linux lo hace. Además, si ejecuta el comando sin la última secuencia de comandos (-e), le da '' server.xml'' sin todo el '' ''. – rmarimon

+0

No funciona para mí en Ubuntu Precise. Inserta texto pero no elimina el ... –

0

me encontré con esta misma necesidad (de ahí la búsqueda de esta pregunta). Después de jugar un poco con sed y awk durante demasiado tiempo, al final me di cuenta de que no hay nada malo en usar una, legible, comprensible lenguaje moderno, ampliamente disponible como Python:

python <<EOF 
    import os, sys, re 
    fname = 'server.xml' 
    os.rename(fname, fname + '.orig') 
    with open(fname + '.orig', 'r') as fin, open(fname, 'w') as fout: 
     data = fin.read() 

     data = re.sub(r'(<!-- BEGIN realm -->).*?(<!-- END realm -->)', 
      r'\1\n' + 
      'insert whatever you want here\n' + 
      r'\2\n', data, flags=re.DOTALL) 
     fout.write(data) 
    EOF 

creo sed y awk han tenido su día.Eran útiles alguna vez, pero muy poca gente puede leer o escribir sin ayuda documental en estos días.

(Fuente: Internet)

0

que no ha podido obtener una solución Dennis facilidad de trabajo en OS X (SED sus BSD es ligeramente diferente). Encontré esta otra solución que pude hacer funcionar tanto en Linux como en OS X (tengo un entorno mixto). La versión original en superuser.com sólo funciona en Linux, aquí me fijo:

lead='^<!-- BEGIN realm -->$' 
tail='^<!-- END realm -->' 
sed -e '/'"$lead"'/,/'"$tail"'/{ /'"$lead"'/{p; r realm.xml' -e' }; /'"$tail"'/p; d;} ' server.xml 

Aquí una versión del código de Dennis que funciona también en OS X (utilizando múltiples líneas):

sed -ne '/'"$lead"'/ { 
p 
r realm.xml 
:a 
n 
/'"$tail"'/ { 
    p 
    b 
} 
ba 
} 
p' server.xml 

Ambos estos códigos imprime la salida en stdout. Use la redirección o, para sustituir el archivo en línea, agregue la opción '-i' (en Linux) o '-i ""' (en BSD/OS X).

Cuestiones relacionadas