Schreiben, verändern oder erstellen einer Temp-Datei

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
ksc
User
Beiträge: 3
Registriert: Sonntag 14. Juni 2015, 10:53

Hallo zusammen

Da ich mich neu angemeldet habe, erst einmal kurz zu mir. Ich bin Netzwerktechniker, daher ist für mich das Programmieren bzw. das Scripten nur ein sehr kleiner Teil meines Jobs. Meist löse ich die Dinge auch in C, Perl oder Bash-Scripts, möchte mich jetzt aber einmal in Python einarbeiten. Habe hier auch schon einige Informationen gefunden und bedanke mich erstmal dafür...

Zum aktuellen Problem bzw. Projekt. Ich schreibe ein Plugin für Icinga2, das verschiedene Informationen von Cisco-Switchen ausliest. Ich schreibe dies selbst, da ich gerne alles in einem Script und möglichst wenig SNMP abfragen erledigen möchte. Ich benutze dazu pysnmp.

Es geht um das schreiben, verändern oder erstellen einer Temp-Datei, hier meine bisherige Lösung.

Code: Alles auswählen

import fileinput

def WriteTemp(Host, Data, Typ, TempDir, Interface):
    LineFound=0

    """
    Testen ob der Ordner existiert, falls nicht erstellen
    """"
    if not os.path.exists(TempDir):
        try:
            os.makedirs(TempDir)
        except (IOError, OSError):
            print('%s, %s', IOError, OSError)
            exit(1)


    """
    Testen ob die TempDatei existiert, falls nicht erstellen, Daten schreiben und zurück.
    """
    if not os.path.isfile(TempDir+"/" + Host + "_" + Typ +".tmp"):
        f= open(TempDir+"/" + Host + "_" + Typ +".tmp", 'w')
        f.write(Data)
        f.close()
        return(0)
        

    """
    Falls die Datei existiert, Linie suchen und ersetzen.
    """
    f=fileinput.FileInput(TempDir+"/" + Host + "_" + Typ +".tmp", inplace =1)
    for line in f:  
        line = line.strip()
        if not line.startswith("'"+Interface+"'"):  
            print(line) 
        else:
            line=line.replace(line, Data)
            print(line)
            #Wenn Linie gefunden, LineFound auf 1
            LineFound=1
    
    """
    Wenn der Datensatz in der Datei nicht existiert, am Dateiende anhängen.
    """     
    if LineFound !=1:
        f= open(TempDir+"/" + Host + "_" + Typ +".tmp", 'a')
        f.write(Data)
        f.close()
    
    return(0)
Die an die Funktionen übergebenen Daten sehen folgendermassen aus.

Code: Alles auswählen

WriteTemp(Host, Data, Typ, TempDir, Interface)
- Host ist einfach eine IP-Adresse oder ein Hostname.
- Data ist eine Liste die in die Tempdatei geschrieben wird, z.B. 'TenGigabitEthernet1/3/2' '1434275311.283667' '1' '1' '328' '10000' '30534717624306' '10833163670508' '9666' '0' '0' '0'
- Typ ist entweder Int für Interface, Net für Netzwerk oder Sys für System
- TempDir ist das Verzeichnis
- Interface der Interfacename z.B. TenGigabitEthernet1/3/2

Diese Lösung funktioniert, jedoch stört mich das doppelte öffnen der Datei wenn die Linie nicht existiert. Ausserdem muss ich mit dieser Lösung jede Linie der Datei bis zum Schluss durchgehen, auch wenn der Datensatz auf der zweiten Linie gefunden wurde. Diese beiden Punkte würde ich gerne ändern, bzw. verbessern und wäre für jeden Tip dankbar.

Hoffe ich habe alle Infos geliefert und mich einigermassen klar ausgedrückt.

Grüsse
Kurt
Sirius3
User
Beiträge: 18335
Registriert: Sonntag 21. Oktober 2012, 17:20

@ksc: fileinput ist ein Modul das ich bisher noch nicht kannte und irgendwie magisch ist. Da würde ich mich an den ersten Abschnitt der Dokumentation halten: "If you just want to read or write one file see open()."

In Python gibt es eine Konvention für Namen, dass alle Variablen und Funktionen klein zu schreiben sind, Klassen mit groß und Konstanten komplett in Großbuchstaben. Wer diese Konvention gewohnt ist, ließt entsprechenden Code viel leichter. Einrücktiefe ist 4 Leerzeichen und nicht 3.

Doc-Strings, also Strings am Anfang einer Funktion, sind zur Dokumentation gedacht. Deine "Doc-Strings" sind aber Kommentare und sollten nicht als Strings geschrieben werden.

Zum zusammensetzen von Pfaden gibt es os.path.join. Der Dateiname sollte auch nur einmal erzeugt und in einer lokalen Variable gespeichert werden, statt ihn 4 mal zu erzeugen.

Funktionen, die das gesamte Programm bei einem Fehler beenden sind ein no-go. Der Aufrufer kann dann auf Fehler gar nicht reagieren. Wenn man nicht sinnvoll auf einen Fehler reagieren kann, ihn einfach weiterreichen.

"return" ist keine Funktion, sollte also nicht wie eine solche geschrieben werden. Was soll überhaupt der Rückgabewert 0? Er hat doch absolut keinen Informationsgehalt.

In Python gibt es für Wahrheitswerte True und False. Die sollte man auch statt 0 und 1 benutzen.

Wenn man etwas in einer Datei ändern will, muß man sie komplett kopieren und den zu ändernden Teil beim Kopieren ersetzen.

Das könnte alles ungefähr so aussehen:

Code: Alles auswählen

import os
 
def write_data(host, data, typ, temp_dir, interface):
    if not os.path.exists(temp_dir):
        os.makedirs(temp_dir)

    filename = os.path.join(temp_dir, "%s_%s.tmp" % (host, typ))
    if not os.path.isfile(filename):
        with open(filename, "w") as f:
            f.write(Data)
        return

    found = False
    try:
        with open(filename, "r") as fin:
            with open(filename+".bak", "w") as fout:
                for line in fin:
                    if not found and line.startswith("'%s'" % interface):
                        found = True
                        fout.write(data + '\n')
                    else:
                        fout.write(line)
                if not found:
                    fout.write(data + '\n')
    except:
        # Fehler beim Schreiben, Aenderungen rueckgaengig machen
        os.remove(filename+'.bak')
        raise
    else:
        # Alles Ok, Aenderungen sichern
        os.rename(filename+'.bak', filename)
ksc
User
Beiträge: 3
Registriert: Sonntag 14. Juni 2015, 10:53

Erstmal vielen Dank für die Antwort und Korrekturen, nach einem solchen Ansatz habe ich gesucht. Da ich's nicht hingekriegt hatte, hab ich nach langem suchen das Modul fileinput verwendet.

Werde mich in Zukunft an die Konventionen halten, beim Einrücken scheine ich beim Kopieren etwas verbockt zu haben, denn der Editor Spyer macht das mit 4 Leerzeichen.

Sehe ich das richtig, dass die Doc-Strings dasselbe sind wie in Perl das POD?

Grüsse und Danke
Kurt
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nach meinem Verstaendnis ist POD ein Dokumentformat, dass an Perl-Dateien als ganzer Block angehaengt wird.
Wenn das stimmt, dann haben die beiden nichts mit einander zu tun (ausser, dass es Unterstuetzung zur on-line Dokumentation gibt): Docstrings werden in Python-Module eingebettet. Ein Modul kann einen Docstring haben (das waere dann dein POD Aequivalent), aber eben auch eine Klasse, Funktion oder Methode.

Die Code-Konventionen werden uebrigens in PEP 8 dokumentiert
ksc
User
Beiträge: 3
Registriert: Sonntag 14. Juni 2015, 10:53

Sehr gut, vielen Dank. Braucht wohl noch etwas Zeit, bis ich Python einigermassen verstehe :)

Grüsse
Kurt
Antworten