次のようにできます:
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))
with ステートメントにより、ファイルが正しく閉じられ、"w"
でファイルが再度開かれます。 モードは、ファイルに書き込む前にファイルを空にします。 re.sub(pattern, replace, string) は、sed/perl の s/pattern/replace/ に相当します。
編集: 例の固定構文
自家製の sed
のオーサリング 純粋な Python で no に置き換える 外部コマンドまたは追加の依存関係は、高貴な地雷を積んだ高貴な仕事です。誰が考えたでしょうか?
それでも実現可能です。 それも望ましいです。私たちは皆、そこに行ったことがあります。「プレーンテキスト ファイルをいくつか変更する必要がありますが、手元にあるのは Python、2 つのプラスチック製の靴ひも、バンカー グレードのマラスキーノ チェリーのカビの生えた缶だけです。助けてください。」
この回答では、以前の回答の素晴らしさを組み合わせた最善のソリューションを提供します。 -すごさ。 plundra が指摘しているように、David Miller のそれ以外の場合の一流の回答は、目的のファイルを非原子的に書き込むため、競合状態を招きます (たとえば、そのファイルを同時に読み取ろうとする他のスレッドやプロセスから)。良くないね。プランドラのそれ以外の点では優れた回答がそれを解決します 多数の致命的なエンコーディング エラー、重大なセキュリティの脆弱性 (元のファイルのアクセス許可やその他のメタデータを保持できない)、正規表現を低レベルの文字インデックスに置き換える時期尚早な最適化など、さらに多くの問題が発生します。それも悪い。
すごいぞ、団結せよ!
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) は足場を作成し、正規表現のみを記述します。まだベータ版ですが、フィードバックをお待ちしています。
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" /etc/apt/sources.list
差分 (前/後) を diff 形式で表示します。
-w オプションを追加して、元のファイルに変更を書き込みます:
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" -w /etc/apt/sources.list
または、API を使用できるようになりました:
>>> import massedit
>>> filenames = ['/etc/apt/sources.list']
>>> massedit.edit_files(filenames, ["re.sub(r'^# deb', 'deb', line)"], dry_run=True)
これは非常に異なるアプローチです。他の回答を編集したくありません.Nested with
私は 3.1 を使用していないため (ここで with A() as a, B() as b:
sources.list を変更するのは少しやり過ぎかもしれませんが、今後の検索のために公開したいと思います。
#!/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)
これにより、他の人がファイルを読み取る競合状態が発生しないようにする必要があります.ああ、正規表現なしでできる場合は str.startswith(...) をお勧めします.