web-dev-qa-db-ger.com

Wie mache ich sed wie Text durch Python ersetzen?

Ich möchte alle geeigneten Repositorys in dieser Datei aktivieren

cat /etc/apt/sources.list
## Note, this file is written by cloud-init on first boot of an instance                                                                                                            
## modifications made here will not survive a re-bundle.                                                                                                                            
## if you wish to make changes you can:                                                                                                                                             
## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg                                                                                                                
##     or do the same in user-data
## b.) add sources in /etc/apt/sources.list.d                                                                                                                                       
#                                                                                                                                                                                   

# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to                                                                                                           
# newer versions of the distribution.                                                                                                                                               
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick main                                                                                                                   
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick main                                                                                                               

## Major bug fix updates produced after the final release of the                                                                                                                    
## distribution.                                                                                                                                                                    
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates main                                                                                                           
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates main                                                                                                       

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu                                                                                                         
## team. Also, please note that software in universe WILL NOT receive any                                                                                                           
## review or updates from the Ubuntu security team.                                                                                                                                 
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick universe                                                                                                               
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick universe                                                                                                           
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates universe
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates universe

## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu 
## team, and may not be under a free licence. Please satisfy yourself as to
## your rights to use the software. Also, please note that software in 
## multiverse WILL NOT receive any review or updates from the Ubuntu
## security team.
# deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick multiverse
# deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick multiverse
# deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates multiverse
# deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates multiverse

## Uncomment the following two lines to add software from the 'backports'
## repository.
## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
# deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse
# deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse

## Uncomment the following two lines to add software from Canonical's
## 'partner' repository.
## This software is not part of Ubuntu, but is offered by Canonical and the
## respective vendors as a service to Ubuntu users.
# deb http://archive.canonical.com/ubuntu maverick partner
# deb-src http://archive.canonical.com/ubuntu maverick partner

deb http://security.ubuntu.com/ubuntu maverick-security main
deb-src http://security.ubuntu.com/ubuntu maverick-security main
deb http://security.ubuntu.com/ubuntu maverick-security universe
deb-src http://security.ubuntu.com/ubuntu maverick-security universe
# deb http://security.ubuntu.com/ubuntu maverick-security multiverse
# deb-src http://security.ubuntu.com/ubuntu maverick-security multiverse

Mit sed ist dies ein einfacher sed -i 's/^# deb/deb/' /etc/apt/sources.list, was ist der eleganteste ("Pythonic") Weg, dies zu tun?

39
Maxim Veksler

massedit.py ( http://github.com/elmotec/massedit ) macht das Gerüst für Sie und lässt nur die Regex zu schreiben. Es ist noch in der Beta, 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)
18
elmotec

Das kannst du so machen:

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 ordnungsgemäß geschlossen wird. Durch das erneute Öffnen der Datei im "w"-Modus wird die Datei geleert, bevor Sie in sie schreiben. resub (pattern, replace, string) ist das Äquivalent von s/pattern/replace/in sed/Perl.

Edit: feste Syntax im Beispiel

46
David Miller

Das Erstellen eines selbstgezüchteten sed Ersatzes in reinem Python mit no externen Befehlen oder zusätzlichen 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 waren alle da, Leute: "Ich muss ein paar Klartextdateien mischen, aber ich habe nur Python, zwei Plastikschnürsenkel und eine schimmelige 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 diese unangenehmen nicht - Großartigkeit zusammenfasst. Wie aus der Plundra hervorgeht, schreibt David Millers ansonsten erstklassige Antwort die gewünschte Datei nicht atomar und lädt daher Race-Bedingungen ein (z. B. von anderen Threads und/oder Prozessen, die versuchen, diese Datei gleichzeitig zu lesen). Das ist schlecht. Plundra's ansonsten exzellente Antwort behebt das Problem, während es noch mehr einführt - einschließlich zahlreicher schwerwiegender Codierungsfehler, einer kritischen Sicherheitslücke (die Berechtigungen und andere Metadaten des Originals bleiben nicht erhalten) Datei) und vorzeitige Optimierung, die reguläre Ausdrücke durch eine einfache Indizierung von Zeichen ersetzt. Das ist auch schlimm.

Großartigkeit, 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')
22
Cecil Curry

Dies ist eine andere Herangehensweise, ich möchte meine andere Antwort nicht bearbeiten. Verschachtelte with, da ich nicht 3.1 (Where with A() as a, B() as b: funktioniert) verwende.

Es könnte ein bisschen übertrieben sein, sources.list zu ändern, aber ich möchte es für zukünftige Suchanfragen dort 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 Leute, die die Datei lesen, keine Race-Bedingungen haben. Oh, und ich bevorzuge str.startswith (...), wenn Sie auf einen regulären Ausdruck verzichten können.

12
plundra

Wenn Sie Python3 verwenden, hilft Ihnen folgendes Modul: https://github.com/mahmoudadel2/pysed

wget https://raw.githubusercontent.com/mahmoudadel2/pysed/master/pysed.py

Fügen Sie die Moduldatei in den Python3-Modulpfad ein.

import pysed
pysed.replace(<Old string>, <Replacement String>, <Text File>)
pysed.rmlinematch(<Unwanted string>, <Text File>)
pysed.rmlinenumber(<Unwanted Line Number>, <Text File>)
6
M. Adel

Versuchen Sie https://pypi.python.org/pypi/pysed

pysed -r '# deb' 'deb' /etc/apt/sources.list

3
dslackw

Wenn Sie wirklich einen sed-Befehl verwenden möchten, ohne ein neues Python-Modul zu installieren, können Sie einfach Folgendes tun: 

import subprocess
subprocess.call("sed command")
2
Brad Jasperson

Sie könnten so etwas tun:

p = re.compile("^\# *deb", re.MULTILINE)
text = open("sources.list", "r").read()
f = open("sources.list", "w")
f.write(p.sub("deb", text))
f.close()

Alternativ (imho, dies ist aus organisatorischer Sicht besser), können Sie Ihren sources.list in Teile (einen Eintrag/ein Repository) aufteilen und sie unter /etc/apt/sources.list.d/ platzieren.

2
barti_ddu

Ich bin nicht sicher über Eleganz, aber dies sollte zumindest gut lesbar sein. Für eine sources.list ist es gut, alle Zeilen vor dem Lesen zu lesen. Für etwas Größeres sollten Sie "in place" ändern, während Sie durchlaufen.

#!/usr/bin/env python
# Open file for reading and writing
with open("sources.list", "r+") as sources_file:
    # Read all the lines
    lines = sources_file.readlines()

    # Rewind and truncate
    sources_file.seek(0)
    sources_file.truncate()

    # Loop through the lines, adding them back to the file.
    for line in lines:
        if line.startswith("# deb"):
            sources_file.write(line[2:])
        else:
            sources_file.write(line)

EDIT: Verwenden Sie with- Anweisung, um die Dateibehandlung zu verbessern. Ich habe auch vergessen, vor dem Abschneiden zurückzuspulen.

2
plundra

Ich wollte in der Lage sein, Text zu finden und zu ersetzen, aber auch übereinstimmende Gruppen in den eingefügten Inhalt einzufügen. Ich habe dieses kurze Skript dazu geschrieben:

https://Gist.github.com/turtlemonvh/0743a1c63d1d27df3f17

Die Schlüsselkomponente davon ist etwas, das so aussieht:

print(re.sub(pattern, template, text).rstrip("\n"))

Hier ist ein Beispiel, wie das funktioniert:

# Find everything that looks like 'dog' or 'cat' followed by a space and a number
pattern = "((cat|dog) (\d+))"

# Replace with 'turtle' and the number. '3' because the number is the 3rd matched group.
# The double '\' is needed because you need to escape '\' when running this in a python Shell
template = "turtle \\3"

# The text to operate on
text = "cat 976 is my favorite"

Das Aufrufen der obigen Funktion mit diesem Ergebnis ergibt:

turtle 976 is my favorite
1
turtlemonvh

Hier ist ein Python-Ein-Modul-Ersatz für Perl -p:

# Provide compatibility with `Perl -p`

# Usage:
#
#     python -mloop_over_stdin_lines '<program>'

# In, `<program>`, use the variable `line` to read and change the current line.

# Example:
#
#         python -mloop_over_stdin_lines 'line = re.sub("pattern", "replacement", line)'

# From the perlrun documentation:
#
#        -p   causes Perl to assume the following loop around your
#             program, which makes it iterate over filename arguments
#             somewhat like sed:
# 
#               LINE:
#                 while (<>) {
#                     ...             # your program goes here
#                 } continue {
#                     print or die "-p destination: $!\n";
#                 }
# 
#             If a file named by an argument cannot be opened for some
#             reason, Perl warns you about it, and moves on to the next
#             file. Note that the lines are printed automatically. An
#             error occurring during printing is treated as fatal. To
#             suppress printing use the -n switch. A -p overrides a -n
#             switch.
# 
#             "BEGIN" and "END" blocks may be used to capture control
#             before or after the implicit loop, just as in awk.
# 

import re
import sys

for line in sys.stdin:
    exec(sys.argv[1], globals(), locals())
    try:
        print line,
    except:
        sys.exit('-p destination: $!\n')
1
Matt McClure

Python hat ein Regex-Modul (Import re). Warum Sie es nicht wie in Perl verwenden möchten. Es verfügt über alle Funktionen eines Perl-Regex

0
Arunmu

Cecil Curry hat eine großartige Antwort, aber seine Antwort funktioniert nur für mehrzeilige reguläre Ausdrücke. Mehrzeilige reguläre Ausdrücke werden seltener verwendet, sind aber manchmal nützlich.

Hier ist eine Verbesserung seiner sed_inplace-Funktion, die es ermöglicht, mit mehrzeiligen regulären Ausdrücken zu arbeiten, wenn Sie dazu aufgefordert werden.

WARNUNG: Im mehrzeiligen Modus wird die gesamte Datei eingelesen und anschließend durch reguläre Ausdrücke ersetzt. Daher sollten Sie diesen Modus nur für kleine ish-Dateien verwenden. Versuchen Sie nicht, dies für Dateien mit einer Größe von Gigabyte auszuführen im mehrzeiligen Modus.

import re, shutil, tempfile

def sed_inplace(filename, pattern, repl, multiline = False):
    '''
    Perform the pure-Python equivalent of in-place `sed` substitution: e.g.,
    `sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`.
    '''
    re_flags = 0
    if multiline:
        re_flags = re.M

    # For efficiency, precompile the passed regular expression.
    pattern_compiled = re.compile(pattern, re_flags)

    # 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:
            if multiline:
                content = src_file.read()
                tmp_file.write(pattern_compiled.sub(repl, content))
            else:
                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)

from os.path import expanduser
sed_inplace('%s/.gitconfig' % expanduser("~"), r'^(\[user\]$\n[ \t]*name = ).*$(\n[ \t]*email = ).*', r'\1John Doe\[email protected]', multiline=True)
0
Keithel