GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie ersetzt man sed wie Text durch Python?

Das geht so:

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

Die with-Anweisung stellt sicher, dass die Datei korrekt geschlossen wird, und das erneute Öffnen der Datei in "w" Der Modus leert die Datei, bevor Sie darauf schreiben. re.sub(pattern, replace, string) ist das Äquivalent von s/pattern/replace/ in sed/perl.

Bearbeiten: feste Syntax im Beispiel


Erstellen eines selbst erstellten sed Ersetzung in reinem Python durch no externe Befehle oder zusätzliche Abhängigkeiten ist eine edle Aufgabe, die mit edlen Landminen beladen ist. Wer hätte das gedacht?

Trotzdem ist es machbar. Es ist auch wünschenswert. Wir alle kennen das schon, Leute:„Ich muss ein paar Klartext-Dateien zerkauen, aber ich habe nur Python, zwei Plastikschnürsenkel und eine verschimmelte Dose Maraschino-Kirschen in Bunkerqualität. Hilfe.“

In dieser Antwort bieten wir eine Best-of-Breed-Lösung, die die Großartigkeit früherer Antworten ohne all das unangenehme nicht zusammenfügt -Großartigkeit. Wie Plundra anmerkt, schreibt die ansonsten erstklassige Antwort von David Miller die gewünschte Datei nicht atomar und lädt daher Race Conditions ein (z. B. von anderen Threads und/oder Prozessen, die versuchen, diese Datei gleichzeitig zu lesen). Das ist schlecht. Plundras ansonsten ausgezeichnete Antwort löst das Problem, während noch mehr eingeführt werden – darunter zahlreiche schwerwiegende Codierungsfehler, eine kritische Sicherheitslücke (die Berechtigungen und andere Metadaten der Originaldatei nicht beibehalten) und eine vorzeitige Optimierung, die reguläre Ausdrücke durch Zeichenindizierung auf niedriger Ebene ersetzt. Das ist auch schlecht.

Großartig, vereinigt euch!

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) erledigt das Gerüst für Sie und lässt nur die Regex zu schreiben. Es befindet sich noch in der Beta-Phase, aber wir suchen nach Feedback.

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

zeigt die Unterschiede (vorher/nachher) im Diff-Format.

Fügen Sie die Option -w hinzu, um die Änderungen in die Originaldatei zu schreiben:

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

Alternativ können Sie jetzt die API verwenden:

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

Dies ist ein so anderer Ansatz, dass ich meine andere Antwort nicht bearbeiten möchte. Verschachtelter with da ich 3.1 nicht verwende (wobei with A() as a, B() as b: funktioniert).

Könnte ein bisschen übertrieben sein, die sources.list zu ändern, aber ich möchte sie für zukünftige Suchen veröffentlichen.

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

Dies sollte sicherstellen, dass andere Personen, die die Datei lesen, keine Race-Conditions haben. Oh, und ich bevorzuge str.startswith(...), wenn Sie auf einen regulären Ausdruck verzichten können.


Linux
  1. Bearbeiten von Text auf der Kommandozeile mit sed

  2. Wie ersetze ich eine Zeichenfolge in einer oder mehreren Dateien?

  3. Wie fügt man Text vor der ersten Zeile einer Datei ein?

  4. Wie verwendet man Sed oder Ex, um einen Block (mehrzeiliger Code) durch einen neuen Textblock (Code) zu ersetzen?

  5. Wie verschiebt man eine Zeile in einer Textdatei um eine Zeile nach oben oder unten?

Bearbeiten von Text mit sed und grep

Wie füge ich mit sed Text in die erste Zeile einer Datei ein?

ersetzt das n-te Vorkommen von String in jeder Zeile einer Textdatei

sed:Wie wird eine Zeile ersetzt, wenn sie gefunden wird, oder an das Ende der Datei angehängt, wenn sie nicht gefunden wird?

Wie kann man Zeichen rekursiv durch sed ersetzen?

Ersetzen Sie Zeilen in einer Datei durch Zeilen in einer anderen durch die Zeilennummer