2010-04-23 12 views
6

Je dois écrire un script simple pour remplacer un bloc de texte dans un fichier de configuration par le contenu d'un autre fichier.Remplacer un bloc de texte délimité dans un fichier par le contenu d'un autre fichier

Supposons avec avoir les fichiers simplifiés suivants:

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

Je veux d'exécuter un script et ont realm.xml remplacer le contenu entre le <!-- BEGIN realm --> et <!-- END realm --> lignes. Si realm.xml change, chaque fois que le script est à nouveau exécuté, il remplacera à nouveau les lignes avec le nouveau contenu de realm.xml. Ceci est destiné à être exécuté dans /etc/init.d/tomcat au démarrage du service sur plusieurs installations sur lesquelles le domaine va être différent. Je ne suis pas sûr comment je peux le faire simplement avec awk ou sed.

Répondre

12

pour cette solution:

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

Whoa ... ça marche. J'essaie de comprendre ce qui se passe. – rmarimon

+3

Les branches 'ba' désignent" a "dans les accolades associées au test de" BEGIN "et les branches' b' se terminent lorsque "END" est trouvé car il se trouve dans un ensemble d'accolades associé à ce test. C'est un peu comme si/BEGIN/alors lisez le fichier; alors que non/FIN/faire sautent la ligne'. –

+0

Je reçois une erreur de syntaxe avec ceci: 'sed: -e expression # 1, char 39: inattendu'} '' –

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, cela n'utilise pas awk ou sed ... Je suppose que ce n'était pas une exigence exclusive :-)

+0

Ce n'était pas une exigence exclusive ;-) – rmarimon

+0

Est-ce que cela fonctionne? TOTAL_LINES aura une valeur qui inclut la chaîne "server.xml" dans la plupart des versions de wc, donc je pense que l'arithmétique échouera. –

+0

@William Pursell - bon point, fixe. –

2

vous pouvez utiliser 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 est passé à awk comme le premier fichier. FNR == NR signifie que les enregistrements du premier fichier sont transmis et stockés dans la variable _. awk traitera le fichier suivant une fois FNR! = NR. si awk trouve /BEGIN realm/, imprimez la ligne BEGIN realm, puis imprimez ce qui est stocké dans _. En définissant un indicateur (f) sur 1, le reste des lignes après BEGIN realm ne sera pas imprimé tant que /END realm/ n'a pas été détecté.

+0

Cela semble être la bonne approche, mais il est très cryptique. Pourriez-vous fournir quelques indices sur la façon dont cela fonctionne? – rmarimon

+0

Comment changer cela afin qu'il puisse faire le remplacement inplace comme "sed -i"? – rmarimon

+0

vous avez juste besoin de rediriger vers le fichier temporaire et le renommer. – ghostdog74

1

Que diriez-vous de ce petit bout que j'ai créé:

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

Les premières commandes imprime les lignes à <!- BEGIN realm --> la deuxième commande imprime la ligne à partir de <!-- END realm --> et les troisièmes commandes ajouter le texte dans le fichier « royaume. xml '. Si seulement je pouvais simplifier la suppression des lignes entre <!- BEGIN realm --> et <!-- END realm --> sans enlever les lignes de repère, il serait aussi simple que cela. Et cela peut être fait inplace avec sed !!!

+0

qu'en est-il de ''? votre commande sed ne remplace pas ''. – ghostdog74

+1

Quand je l'exécute dans ma machine Linux, c'est le cas. De plus, si vous exécutez la commande sans le dernier script (-e), elle donne '' server.xml'' sans tous les '' ''. – rmarimon

+0

Ne fonctionne pas pour moi sur Ubuntu Precise. Insère du texte mais ne supprime pas le ... –

1

Vous pouvez également utiliser la commande ed (voir 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

je suis tombé sur ce même besoin (donc de trouver cette question). Après déconner avec sed et awk pour beaucoup trop longtemps, j'ai finalement pris conscience qu'il n'y a rien de mal à utiliser un langage moderne, lisible, compréhensible, largement disponible comme 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 

Je pense que sed et awk ont ​​eu leur jour.Ils étaient utiles une fois, mais très peu de gens peuvent lire ou écrire sans aide documentaire ces jours-ci.

(Source: Internet)

0

je ne pouvais pas obtenir la solution Dennis travailler facilement sur OS X (ses sed BSD est légèrement différente). J'ai trouvé cette autre solution que j'ai pu faire fonctionner sur Linux et OS X (j'ai un environnement mixte). La version originale sur superuser.com ne fonctionne que sur Linux, ici je l'ai fixé:

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

Voici une version de code de Dennis qui fonctionne également sur Mac OS X (en utilisant plusieurs lignes):

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

Ces deux codes imprime la sortie sur stdout. Utilisez la redirection ou, pour remplacer le fichier en ligne, ajoutez l'option '-i' (sur linux) ou '-i ""' (sur BSD/OS X).

Questions connexes