GNU/Linux >> Belajar Linux >  >> Linux

Bagaimana melakukan sed like text ganti dengan python?

Anda dapat melakukannya seperti ini:

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

Pernyataan with memastikan bahwa file ditutup dengan benar, dan membuka kembali file di "w" mode mengosongkan file sebelum Anda menulisnya. re.sub(pattern, replace, string) setara dengan s/pattern/replace/ di sed/perl.

Edit: memperbaiki sintaks dalam contoh


Membuat sed buatan sendiri penggantian dengan Python murni dengan no perintah eksternal atau ketergantungan tambahan adalah tugas mulia yang sarat dengan ranjau darat yang mulia. Siapa sangka?

Meskipun demikian, itu layak. Itu juga diinginkan. Kita semua pernah ke sana, orang-orang:"Saya perlu membuat beberapa file teks biasa, tetapi saya hanya memiliki Python, dua tali sepatu plastik, dan sekaleng ceri Maraschino kelas bunker yang berjamur. Tolong."

Dalam jawaban ini, kami menawarkan solusi terbaik yang menyatukan kehebatan jawaban sebelumnya tanpa semua yang tidak menyenangkan tidak -keangkeran. Sebagai catatan plundra, jawaban terbaik David Miller menulis file yang diinginkan secara non-atomik dan karenanya mengundang kondisi balapan (misalnya, dari utas dan/atau proses lain yang mencoba membaca file itu secara bersamaan). Itu buruk. Jawaban Plundra yang luar biasa memecahkan itu masalah sambil memperkenalkan lebih banyak lagi – termasuk banyak kesalahan penyandian fatal, kerentanan keamanan kritis (gagal mempertahankan izin dan metadata lain dari file asli), dan pengoptimalan prematur menggantikan ekspresi reguler dengan pengindeksan karakter tingkat rendah. Itu juga buruk.

Kedahsyatan, bersatu!

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) melakukan scaffolding untuk Anda hanya menyisakan regex untuk menulis. Ini masih dalam versi beta tetapi kami mengharapkan masukan.

python -m massedit -e "re.sub(r'^# deb', 'deb', line)" /etc/apt/sources.list

akan menunjukkan perbedaan (sebelum/sesudah) dalam format diff.

Tambahkan opsi -w untuk menulis perubahan pada file asli:

python -m massedit -e "re.sub(r'^# deb', 'deb', line)" -w /etc/apt/sources.list

Atau, Anda sekarang dapat menggunakan api:

>>> import massedit
>>> filenames = ['/etc/apt/sources.list']
>>> massedit.edit_files(filenames, ["re.sub(r'^# deb', 'deb', line)"], dry_run=True)

Ini adalah pendekatan yang berbeda, saya tidak ingin mengedit jawaban saya yang lain. Bersarang with karena saya tidak menggunakan 3.1 (Di mana with A() as a, B() as b: berfungsi).

Mungkin sedikit berlebihan untuk mengubah sources.list, tapi saya ingin meletakkannya di sana untuk pencarian di masa mendatang.

#!/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)

Ini harus memastikan tidak ada kondisi balapan dari orang lain yang membaca file tersebut. Oh, dan saya lebih suka str.startswith(...) ketika Anda dapat melakukannya tanpa regexp.


Linux
  1. Memanipulasi teks pada baris perintah dengan sed

  2. Bagaimana Cara Memindahkan Garis Dalam File Teks Ke Atas Atau Ke Bawah Dengan Satu Baris?

  3. Ganti Rentang Garis Dengan Rentang Garis (sed Atau Lainnya)?

  1. Bagaimana Mengurai Setiap Baris File Teks Sebagai Argumen Untuk Perintah?

  2. Beberapa File Teks Ganti Dengan Sed?

  3. Bagaimana cara menyisipkan teks di awal file?

  1. Bagaimana Cara Mengganti String Dalam File?

  2. Bagaimana cara memasukkan teks ke baris pertama file menggunakan sed?

  3. gema teks dengan baris baru di bash