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
.
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