2010-04-23 14 views
6

ho bisogno di scrivere un semplice script per sostituire un blocco di testo in un file di configurazione con il contenuto di un altro file.Sostituire blocco delimitata di testo in file con il contenuto di un altro file

Assumiamo con i seguenti file semplificate:

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"/> 

voglio eseguire uno script e hanno realm.xml sostituire il contenuto tra il <!-- BEGIN realm --> e <!-- END realm --> Linee. Se lo realm.xml cambia quindi ogni volta che lo script viene eseguito di nuovo, sostituirà nuovamente le righe con il nuovo contenuto di realm.xml. Questo è destinato a essere eseguito in /etc/init.d/tomcat all'avvio del servizio su più installazioni su cui il dominio sarà diverso.

Non sono sicuro di come farlo semplicemente con awk o sed.

risposta

12

dare una prova:

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

Whoa ... funziona. Sto cercando di capire come funzionano le ramificazioni per capire veramente cosa sta succedendo. – rmarimon

+3

I rami 'ba' per etichettare" a "all'interno delle parentesi associate al test per" BEGIN "e i rami' b' alla fine quando viene trovata "END" poiché si trova in una serie di parentesi associate a quel test. È un po 'come "se/BEGIN/poi leggi il file; mentre non/END/fai saltare la linea'. –

+0

Viene visualizzato un errore di sintassi con questo: 'sed: -e espressione # 1, char 39: imprevisto'} '' –

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, questo non fa uso di awk o sed ... ho pensato che non era un requisito esclusivo :-)

+0

Non era un requisito esclusivo ;-) – rmarimon

+0

Funziona? TOTAL_LINES avrà un valore che include la stringa "server.xml" nella maggior parte delle versioni di wc, quindi sospetto che l'aritmetica fallirà. –

+0

@William Pursell - buon punto, corretto. –

2

è possibile utilizzare 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 è passato a awk come primo file. FNR == NR significa ottenere i record del primo file passati e memorizzare nella variabile _. awk elaborerà il file successivo una volta FNR! = NR. se awk trova /BEGIN realm/, stampa la riga BEGIN realm, quindi stampa ciò che è memorizzato in _. Impostando un flag (f) su 1, il resto delle righe dopo BEGIN realm non verrà stampato fino a quando non viene rilevato /END realm/.

+0

Questo sembra l'approccio giusto ma è molto criptico. Potresti fornire alcuni indizi su come funziona? – rmarimon

+0

Come cambierebbe questo in modo che possa sostituire la sostituzione come "sed -i"? – rmarimon

+0

devi solo reindirizzare al file temporaneo e rinominarlo. – ghostdog74

1

Che ne dite di questo piccolo frammento che ho creato:

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

I primi comandi di stampare le linee fino a <!- BEGIN realm --> il secondo comando stampa la linea a partire da <!-- END realm --> e il terzo comandi aggiungere il testo nel regno file'. xml'. Se solo potessi semplificare la rimozione delle linee tra <!- BEGIN realm --> e <!-- END realm --> senza rimuovere le linee dei marker, sarebbe semplice come si ottiene. E può essere fatto inplace con sed !!!

+0

che dire di ''? il tuo comando sed non sostituisce ''. – ghostdog74

+1

Quando lo eseguo nella mia macchina Linux lo fa. Inoltre, se si esegue il comando senza l'ultimo script (-e) si fornisce il '' server.xml'' senza tutti gli '' ''. – rmarimon

+0

Non funziona per me su Ubuntu Precise. Inserisce il testo ma non rimuove lo ... –

1

È inoltre possibile utilizzare il comando ed (cfr http://wiki.bash-hackers.org/howto/edit-ed):

cat <<-'EOF' | sed -e 's/^ *//' -e 's/ *$//' | ed -s server.xml 
    H 
    /BEGIN realm/i 
    . 
    /BEGIN realm/+1,/END realm/-1d 
    .-1r realm.xml 
    wq 
EOF 
0

mi sono imbattuto in questa stessa esigenza (da qui trovare questa domanda). Dopo fare in giro con sed e awk per troppo tempo, alla fine ho capito che non c'è niente di sbagliato con l'utilizzo di un leggibile, comprensibile, linguaggio moderno, ampiamente disponibile come 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 

penso sed e awk hanno fatto il loro tempo.Erano utili una volta, ma pochissime persone possono leggere o scrivere senza assistenza documentaria in questi giorni.

(Fonte: internet)

0

sono stato in grado di ottenere la soluzione Dennis facilmente lavorando su OS X (i suoi sed BSD è leggermente diverso). Ho trovato quest'altra soluzione che sono riuscito a lavorare su Linux e OS X (ho un ambiente misto). La versione originale su superuser.com funziona solo su Linux, qui ho riparato:

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

Ecco una versione del codice di Dennis che funziona anche su OS X (con più linee):

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

Entrambi questi codici stampa l'output su stdout. Usa il reindirizzamento o, per sostituire il file in linea, aggiungi l'opzione '-i' (su linux) o '-i ""' (su BSD/OS X).

Problemi correlati