GNU/Linux >> Znalost Linux >  >> Linux

Jak nahradit text podobný sedu pythonem?

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.


Linux
  1. Nahradit rozsah řádků rozsahem řádků (sed nebo jiné)?

  2. Jak analyzovat každý řádek textového souboru jako argument příkazu?

  3. Jak vložit text na začátek souboru?

  1. Jak vložím text do 1. řádku souboru pomocí sed?

  2. echo text s novým řádkem v bash

  3. sed:jak nahradit řádek, pokud byl nalezen, nebo připojit na konec souboru, pokud nebyl nalezen?

  1. Manipulace s textem na příkazovém řádku pomocí sed

  2. Jak rekurzivně nahradit znaky sed?

  3. nahradit řádky v jednom souboru řádky v jiném číslem řádku