2010-12-13 17 views
33

Ich möchte alle apt-Repositories in dieser DateiWie sed wie Text ersetzen mit Python zu ersetzen?

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 ermöglichen sed dies ist ein einfaches sed -i 's/^# deb/deb/' /etc/apt/sources.list was ist das eleganteste („pythonic“) Weg, dies zu tun?

+0

pythonpy (https://github.com/russell91/pythonpy) gibt Ihnen eine gute Möglichkeit, mit der Kommandozeile zu interagieren: 'cat /etc/apt/sources.list | py -x 're (r "^ # deb", "deb", x) " – RussellStewart

Antwort

15

massedit.py (http://github.com/elmotec/massedit) macht das Gerüst für Sie 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.

die Option -w Fügen Sie die Änderungen an der Originaldatei zu schreiben:

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

Alternativ können Sie nun die api verwenden:

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

Sieht gut aus. Vielen Dank. Ich wäre großartig, wenn Sie eine Wiki-Seite bei github erstellen könnten, die ein API-Verwendungsbeispiel zeigt, was bedeutet, dass Sie die Bibliothek aus einem bestehenden Python-Skript aufrufen. –

+1

Ich habe das Modul erweitert, um die Verwendung als Bibliothek zu erleichtern (Danke, dass Sie darauf hingewiesen haben). Es gibt ein Beispiel in der Liesmich auf Github jetzt. – elmotec

+0

@MaximVeksler Dies scheitert im folgenden Fall 's/^ Q (. *) /" & "/': Sie ersetzen die Übereinstimmung mit dem Literal '" & "' anstelle der beabsichtigten Aufgabe, die Übereinstimmung mit Anführungszeichen zu umgeben – Adrian

0

Python hat ein Regex-Modul (Import re). warum willst du es nicht wie in Perl machen? Es hat alle Eigenschaften eines Perl Regex

+0

Nicht sicher, was du fragst? Ich evaluiere die Portierung unserer Skripte auf Python und bin neu in der Sprache. Ich habe mich gefragt, was ist die Syntax für eine grundlegende Operation, z. Textdateiinhalt: Lesen, Ersetzen, Schreibvorgang. (Ich habe sed als Dienstprogramm verwendet, um zu erklären, was ich tun möchte). –

+0

@ Maxim Portierung von was? –

+0

@David Miller: bash, sed, awk ... –

2

Nicht sicher über elegant, aber das sollte zumindest ziemlich lesbar sein. Für eine sources.list ist es in Ordnung, alle Zeilen vor der Hand zu lesen, für etwas Größeres, das Sie "in Place" ändern möchten, während Sie es 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 für eine bessere Datei-Handling. Ich habe auch vergessen zurückzuspulen bevor ich vorher abgeschnitten habe.

+0

Ich lese gerade die Datei, schließe sie, öffne sie im Schreibmodus und schreibe die modifizierte Version. Das spart Sorgen um Suchen und Abschneiden. –

+0

@Thomas, ja. Fühlt nicht, dass Python :-P Dachte, es mit einer Tempdatei zu machen und es dann auch an Ort und Stelle zu bewegen, um atomar zu sein (-ish). – plundra

+0

Ich weiß nicht, dass es eine pythonische Möglichkeit gibt, eine Datei an Ort und Stelle zu ändern. Die tempfile-Idee hat jedoch einige Vorteile. –

35

Sie können das tun, wie folgt:

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 mit Anweisung stellt sicher, dass die Datei korrekt geschlossen ist, und erneutes Öffnen der Datei in "w" Modus leert die Datei, bevor Sie zu schreiben. re_ (pattern, replace, string) ist das Äquivalent zu s/pattern/replace/in sed/perl.

Edit: feste Syntax in Beispiel

+1

Gute Idee mit 'mit', aber mit diesem werden Sie einfach die neue sources.list an die alte anhängen. – plundra

+0

Das sieht gut aus (in der Syntax), aber es hat die Datei dupliziert. Muss ich eine Kürzung vornehmen? Wird die gesamte Datei in den Arbeitsspeicher geladen, oder wird die Zeile zeilenweise "streaming"? –

+0

@plundra yep, daft Fehler, dass :) Bearbeitet zu ammend ... –

2

Sie etwas tun könnte, wie:

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, das ist besser aus organisatorischer Sicht) können Sie Ihre sources.list in Stücke (ein Eintrag spalten könnte/Ein Repository) und platzieren Sie sie unter /etc/apt/sources.list.d/

11

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

Könnte ein bisschen übertrieben sein, um sources.list zu ändern, aber ich möchte es für zukünftige Suchanfragen 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 keine Race-Bedingungen von anderen Menschen die Datei lesen zu gewährleisten. Oh, und ich bevorzuge str.startswith (...), wenn Sie auf eine Regexp verzichten können.

+0

Ich bekomme total den Wunsch, Regex wo immer möglich einzubeziehen :) In der Zwischenzeit: mit, str.startswith() und NamedTemporaryFile zeigen die Art der Batterien-inklusive Ansatz von Python, die es machen so nützlich viel Zeit für einfache Aufgaben wie diese. –

+0

Aus Interesse, warum haben Sie 'shuthil.move' anstelle von' os.rename' verwendet? –

+2

@Mark Longair: 'os.rename' funktioniert nicht zwischen Dateisystemen. Wenn '/ tmp' zum Beispiel auf' tmpfs' steht, würde es fehlschlagen. – plundra

1

Hier ist ein One-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') 
5

Wenn Sie Python3 verwenden, wird das folgende Modul Ihnen helfen: https://github.com/mahmoudadel2/pysed

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

Legen Sie das Modul-Datei in Ihre Python3 Module Pfad, dann:

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

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

einen homegrown sed Ersatz in reinem Python Authoring mit keine externen Befehlen oder zusätzliche Abhängigkeiten ist eine edle Aufgabe beladen mit edlen Minen. Wer hätte das gedacht?

Trotzdem ist es machbar. Es ist auch wünschenswert. Wir waren alle schon da, Leute: "Ich muss ein paar Klartext-Dateien verschleppen, aber ich habe nur Python, zwei Plastikschnürsenkel und eine schimmelige Dose Bunker-Maraschino-Kirschen. Hilfe."

In dieser Antwort bieten wir eine Best-of-Breed-Lösung zusammen, die die Großartigkeit der früheren Antworten ohne all die unangenehmen nicht -awesomeness cobbling zusammen. Als Plundra-Notizen schreibt David Miller's otherwise top-notch answer die gewünschte Datei nicht-atomar und lädt daher Rennbedingungen ein (z. B. von anderen Threads und/oder Prozessen, die versuchen, diese Datei gleichzeitig zu lesen). Das ist schlecht. Plundras otherwise excellent answer löst das Problem bei der Einführung noch mehr - einschließlich zahlreicher fataler Kodierungsfehler, eine kritische Sicherheitslücke (nicht erhalten die Berechtigungen und andere Metadaten der ursprünglichen Datei) und vorzeitige Optimierung ersetzen reguläre Ausdrücke durch Low-Level-Zeichenindexierung. Das ist auch schlecht.

Awesomeness, 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') 
+1

Das scheitert im folgenden Fall 's/^ Q (. *) /" & "/': Sie ersetzen die Übereinstimmung durch das Literal '" & "' anstelle der beabsichtigten Aufgabe, die Übereinstimmung mit Anführungszeichen zu umgeben – Adrian

1

Ich wollte in der Lage sein, Text zu finden und zu ersetzen, aber auch passende Gruppen in den Inhalt, den ich einfügen.Ich schrieb dieses kurzes Skript zu tun, dass:

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

Die Schlüsselkomponente das ist etwas, das wie wie folgt aussieht:

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

Hier ist ein Beispiel dafür, 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" 

Der Aufruf der obigen Funktion mit dieser ergibt:

turtle 976 is my favorite