Můžete to udělat takto:
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))
Příkaz with zajišťuje správné uzavření souboru a opětovné otevření souboru v "w"
režim vyprázdní soubor, než do něj zapíšete. re.sub(vzor, nahradit, řetězec) je ekvivalentem s/vzor/nahradit/ v sed/perl.
Upravit: opravená syntaxe v příkladu
Vytvoření domácího sed
nahrazení v čistém Pythonu za ne externí příkazy nebo další závislosti je ušlechtilý úkol naložený ušlechtilými nášlapnými minami. Kdo by si to pomyslel?
Nicméně je to proveditelné. Je to také žádoucí. Všichni jsme tam byli, lidi:"Potřebuji ukočírovat nějaké soubory ve formátu prostého textu, ale mám jen Python, dvě plastové tkaničky a plesnivou plechovku třešní Maraschino v bunkrové kvalitě. Pomoc."
V této odpovědi nabízíme nejlepší řešení, které spojuje úžasnost předchozích odpovědí bez toho nepříjemného ne -Úžasnost. Jak poznamenává plundra, jinak špičková odpověď Davida Millera zapisuje požadovaný soubor neatomicky, a proto vyzývá závody (např. z jiných vláken a/nebo procesů pokoušejících se souběžně číst tento soubor). To je špatné. Jinak skvělá odpověď Plundry to řeší problém při zavádění ještě dalšího – včetně mnoha fatálních chyb kódování, kritické bezpečnostní chyby (neschopnost zachovat oprávnění a další metadata původního souboru) a předčasné optimalizace nahrazující regulární výrazy nízkoúrovňovým indexováním znaků. To je také špatné.
Úžasné, spojte se!
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) za vás udělá lešení a nechá zapsat pouze regulární výraz. Je stále ve verzi beta, ale čekáme na zpětnou vazbu.
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" /etc/apt/sources.list
zobrazí rozdíly (před/po) ve formátu diff.
Přidejte volbu -w pro zápis změn do původního souboru:
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" -w /etc/apt/sources.list
Případně můžete nyní použít api:
>>> import massedit
>>> filenames = ['/etc/apt/sources.list']
>>> massedit.edit_files(filenames, ["re.sub(r'^# deb', 'deb', line)"], dry_run=True)
Toto je tak odlišný přístup, nechci upravovat svou další odpověď. Nested with
protože nepoužívám 3.1 (kde with A() as a, B() as b:
funguje).
Může být trochu přehnané měnit sources.list, ale chci to tam dát pro budoucí vyhledávání.
#!/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)
To by mělo zajistit, že ostatní lidé nebudou číst soubor žádné rasy. A já preferuji str.startswith(...), když se obejdete bez regexpu.