GNU/Linux >> Linux Esercitazione >  >> Linux

Come sostituire il testo come sed con python?

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.


Linux
  1. Come sostituire una stringa in un file?

  2. Come sostituire una stringa con una stringa contenente una barra con Sed?

  3. Come spostare una riga in un file di testo su o giù di una riga?

  4. Sostituire Intervallo di linee con Intervallo di linee (sed o altro)?

  5. Sostituisci più file di testo con Sed?

Manipolazione del testo con sed e grep

Come usare sed per sostituire la variabile di un file di configurazione?

sostituire l'ennesima occorrenza di stringa in ogni riga di un file di testo

Come sostituire un file in jar con la riga di comando in Linux?

sed:come sostituire la riga se trovata o aggiungere alla fine del file se non trovata?

sostituire le righe in un file con le righe in un altro per numero di riga