Puoi farlo in questo modo:
with open("/etc/apt/sources.list", "r") as sources:
lines = sources.readlines()
with open("/etc/apt/sources.list", "w") as sources:
for line in lines:
sources.write(re.sub(r'^# deb', 'deb', line))
L'istruzione with garantisce che il file sia chiuso correttamente e riapre il file in "w"
mode svuota il file prima di scriverci sopra. re.sub(pattern, replace, string) è l'equivalente di s/pattern/replace/ in sed/perl.
Modifica: sintassi corretta nell'esempio
Creazione di un sed
locale sostituzione in puro Python con no comandi esterni o dipendenze aggiuntive è un compito nobile carico di nobili mine antiuomo. Chi l'avrebbe mai detto?
Tuttavia, è fattibile. È anche auspicabile. Ci siamo passati tutti, gente:"Ho bisogno di mungere alcuni file di testo in chiaro, ma ho solo Python, due lacci di plastica e una lattina ammuffita di ciliegie al maraschino di qualità bunker. Aiuto."
In questa risposta, offriamo una soluzione ottimale mettendo insieme la bellezza delle risposte precedenti senza tutto quello spiacevole no -grandiosità. Come osserva plundra, la risposta altrimenti di prim'ordine di David Miller scrive il file desiderato in modo non atomico e quindi invita condizioni di competizione (ad esempio, da altri thread e/o processi che tentano di leggere contemporaneamente quel file). Questo è male. La risposta altrimenti eccellente di Plundra risolve quello problema introducendo ancora di più, inclusi numerosi errori di codifica fatali, una vulnerabilità di sicurezza critica (mancata conservazione delle autorizzazioni e altri metadati del file originale) e ottimizzazione prematura che sostituisce le espressioni regolari con l'indicizzazione dei caratteri di basso livello. Anche questo è negativo.
Meraviglia, unitevi!
import re, shutil, tempfile
def sed_inplace(filename, pattern, repl):
'''
Perform the pure-Python equivalent of in-place `sed` substitution: e.g.,
`sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`.
'''
# For efficiency, precompile the passed regular expression.
pattern_compiled = re.compile(pattern)
# For portability, NamedTemporaryFile() defaults to mode "w+b" (i.e., binary
# writing with updating). This is usually a good thing. In this case,
# however, binary writing imposes non-trivial encoding constraints trivially
# resolved by switching to text writing. Let's do that.
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_file:
with open(filename) as src_file:
for line in src_file:
tmp_file.write(pattern_compiled.sub(repl, line))
# Overwrite the original file with the munged temporary file in a
# manner preserving file attributes (e.g., permissions).
shutil.copystat(filename, tmp_file.name)
shutil.move(tmp_file.name, filename)
# Do it for Johnny.
sed_inplace('/etc/apt/sources.list', r'^\# deb', 'deb')
massedit.py (http://github.com/elmotec/massedit) fa l'impalcatura per te lasciando solo la regex da scrivere. È ancora in versione beta ma stiamo cercando feedback.
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" /etc/apt/sources.list
mostrerà le differenze (prima/dopo) in formato diff.
Aggiungi l'opzione -w per scrivere le modifiche al file originale:
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" -w /etc/apt/sources.list
In alternativa, ora puoi usare l'API:
>>> import massedit
>>> filenames = ['/etc/apt/sources.list']
>>> massedit.edit_files(filenames, ["re.sub(r'^# deb', 'deb', line)"], dry_run=True)
Questo è un approccio così diverso, non voglio modificare la mia altra risposta. with
annidato dato che non uso 3.1 (Where with A() as a, B() as b:
funziona).
Potrebbe essere un po' eccessivo modificare sources.list, ma voglio pubblicarlo per ricerche future.
#!/usr/bin/env python
from shutil import move
from tempfile import NamedTemporaryFile
with NamedTemporaryFile(delete=False) as tmp_sources:
with open("sources.list") as sources_file:
for line in sources_file:
if line.startswith("# deb"):
tmp_sources.write(line[2:])
else:
tmp_sources.write(line)
move(tmp_sources.name, sources_file.name)
Ciò dovrebbe garantire l'assenza di condizioni di competizione per altre persone che leggono il file. Oh, e preferisco str.startswith(...) quando puoi fare a meno di una regexp.