Trouver et remplacer tout entre deux espaces réservés avec le contenu d’une variable

Aloha, j’ai essayé de comprendre comment remplacer / insérer des chaînes de texte entre deux emplacements.

#start REPLACE ANYTHING IN HERE #end 

A l’origine, j’essayais de faire cela avec BASH via sed, mais j’ai heurté un road-block lorsque j’ai essayé de transmettre une variable à sed.

 sed -n -i '/#start/{p;:a;N;/#end/!ba;s/.*\n/hello\n/};p' file.txt 

Résultats

 #start hello #end 

mais pas de joie quand j’essaye

 sed -n -i '/#start/{p;:a;N;/#end/!ba;s/.*\n/$replace_var\n/};p' file.txt 

ou

 sed -n -i "/#start/{p;:a;N;/#end/!ba;s/.*\n/$replace_var\n/};p" file.txt 

J’ai été là pendant des heures et j’ai cherché autour de moi mais je n’ai pas trouvé de solution. Je suis en train d’essayer en python ou dans une autre langue, ou peut-être avec awk. Je suis un peu nouveau dans ce domaine, donc toute information utile serait appréciée.

Merci d’avance

C’est ce que je suis allé avec à la fin. C’est un script qui, associé à cron, met à jour mon fichier /var/etc/hosts.deny avec la dernière liste de blocage ssh publiée.

 import re import urllib2 hosts_deny = open('/etc/hosts.deny','r+') hosts_deny_text = hosts_deny.read() blockedHosts = urllib2.urlopen('http://www.openbl.org/lists/hosts.deny').read() place = re.comstack('(?<=#start)(\r?\n)' '(.*?)' '(?=\r?\n#end)',re.DOTALL)#DOTALL enables '.' to also include #a new line hosts_deny_text = re.sub(place, '\n'+ blockedHosts, hosts_deny_text) hosts_deny.seek(0) hosts_deny.write(hosts_deny_text) hosts_deny.close() 

Compte tenu de ce que vous expliquez, je ne peux que proposer ce code simple:

 import re ss = '''qslkjqskqsdhf #start REPLACE ANYTHING IN HERE #end 2135468761265 ''' reg = re.comstack('(?<=#start)(\r?\n)' '(.*?)' '(?=\r?\n#end)',re.DOTALL) print ss print '----' print reg.sub('\\1Ia orana',ss) 

résultat

 qslkjqskqsdhf #start REPLACE ANYTHING IN HERE #end 2135468761265 ---- qslkjqskqsdhf #start Ia orana #end 2135468761265 

Cela semble faire ce que vous voulez:

 sed -ie "/#start/,/#end/{/#start/b;/#end/b;s/.*/$replace_var/;}" file.txt 

Les parameters /#start/b et /#end/b ignorent ces lignes, sinon vous les remplacerez également.

Vous pouvez lire le fichier dans une chaîne puis faire:

 sstart = s.split(start) for i in range(len(s)): if i%2 ==1: send = sstart[i].split(end) for i in range(len(send)): if i%2 == 0: send[i] = REPLACEMENT sstart[i] = send.join() s = sstart.join() 

Vous parcourez donc la liste en coupant la partie qui doit être remplacée, puis en recollant les pièces.

Avec une expression rationnelle “dotall”, c’est facile. Ce sont faciles avec Perl, Python, PCRE, etc. Par exemple, en Python:

 >>> s = '''#start ... REPLACE ANYTHING IN HERE ... #end''' >>> re.sub(r'(?s)(#start\n).*?\n(#end)', r'\1hello\n\2', s) '#start\nhello\n#end' 

Apparemment, faire correspondre les lignes de début et de fin et les remplacer par elles-mêmes est excessif, mais j’ai décidé de le garder général si vous vouliez l’étendre davantage.

J’ai utilisé le (?s) lieu de passer un indicateur re.DOTALL pour que tout soit autonome, et vous ne devriez pas penser aux différences entre les indicateurs de transmission de Perl, Python, etc. Mais dans la vraie vie, il est généralement plus facile d’utiliser les drapeaux au lieu de les incorporer.

Je pense que sed est plutôt inadapté à cette tâche, j’utiliserais plutôt awk:

 awk '!f; /#start/ { f=1; print repl } /#end/ { f=0; print }' repl="$replace_var" file.txt 

La variable f est un drapeau qui garde une trace du moment où nous sums dans les marqueurs. !f appelle le bloc par défaut ( {print $0} ) et imprime tout en dehors des marqueurs, y compris le marqueur #start .

Essai

Fichier de test copié à partir de la réponse d’ eyquem :

 cat << EOF > file.txt qslkjqskqsdhf #start REPLACE ANYTHING IN HERE #end 2135468761265 EOF 

Remplacez le contenu inter-marqueur par hello\nhello :

 awk '!f; /#start/ { f=1; print repl } /#end/ { f=0; print }' repl="$(printf 'hello\nhello')" file.txt 

Sortie:

 qslkjqskqsdhf #start hello hello #end 2135468761265