Wie mache ich sed wie Text durch Python ersetzen?




regex linux (10)

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 eine einfache sed -i 's/^# deb/deb/' /etc/apt/sources.list Was ist der eleganteste ("pythonic") Weg, dies zu tun?


Answer #1

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


Answer #2

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)

Answer #3

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

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 Personen, die die Datei lesen, keine Rennbedingungen haben. Oh, und ich bevorzuge str.startswith (...), wenn man auf einen Regex verzichten kann.


Answer #4

Hier ist ein Ein-Modul-Python-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')

Answer #5

Die Erstellung eines selbst entwickelten sed Ersatzes in reinem Python ohne 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 waren alle dort, Leute: "Ich muss ein paar Klartextdateien mungern, aber ich habe nur Python, zwei Plastikschnürsenkel und eine verschimmelte Dose Bunker-Maraschino-Kirschen. Hilfe."

In dieser Antwort bieten wir eine Best-of-Breed-Lösung, bei der die Awomen früherer Antworten miteinander kombiniert werden, ohne all diese unangenehmen Unbequemlichkeiten. Wie Plundra feststellt, schreibt die ansonsten erstklassige Antwort von David Miller die gewünschte Datei nicht atomar und ruft daher Race-Bedingungen ein (z. B. von anderen Threads und / oder Prozessen, die versuchen, diese Datei gleichzeitig zu lesen). Das ist schlecht. Die ansonsten hervorragende Antwort von Plundra behebt dieses Problem und führt noch mehr ein. Dazu gehören zahlreiche schwerwiegende Codierungsfehler, eine kritische Sicherheitsanfälligkeit (die Berechtigungen und andere Metadaten der Originaldatei werden nicht erhalten) sowie eine vorzeitige Optimierung, bei der reguläre Ausdrücke durch die Indizierung von Zeichen auf niedriger Ebene ersetzt werden. Das ist auch schlimm.

Großartigkeit, vereint!

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

Answer #6

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

Answer #7

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. Wenn Sie die Datei erneut im Modus "w" öffnen, wird die Datei geleert, bevor Sie darauf schreiben. resub (pattern, replace, string) ist das Äquivalent zu s / pattern / replace / in sed / perl.

Edit: Feste Syntax im Beispiel


Answer #8

Ich bin nicht sicher über Eleganz, aber dies sollte zumindest ziemlich lesbar sein. Für eine sources.list ist es in Ordnung, 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)

BEARBEITEN: Verwenden Sie zusammen with -statement die Dateibearbeitung. Ich habe auch vergessen, vor dem Abschneiden zurückzuspulen.



Answer #10

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 Ihre sources.list in Stücke (einen Eintrag / ein Repository) /etc/apt/sources.list.d/ und sie unter /etc/apt/sources.list.d/





linux