.cfg Daten auswerten

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
kimaro
User
Beiträge: 5
Registriert: Donnerstag 9. Oktober 2014, 09:16

Moin,

habe erst vor kurzem angefangen mit python und habe jetzt ein Problem mit dem ich einfach nicht mehr weiterkomme.
Ich möchte in einer cfg datei nach einem Abschnitt suchen und diesen in eine weitere Datei schreiben.
Der Abschnitt geht über 3-15 Zeilen und ist von Datei zu Datei unterschiedlich.
Der Abschnitt fängt mit "{" an und hört mit "}" auf.

Vielen Dank schon mal im Vorraus
BlackJack

@kimaro: Was ist denn das konkrete Problem dabei? Nach der bisherigen Beschreibung musst Du doch nur die Datei in eine Zeichenkette einlesen, die Positionen beiden Klammern finden, den Teil ”ausschneiden”, und in eine neue Datei schreiben. Zum finden der Position haben Zeichenketten die `index()`-Methode. ”Ausschneiden” geht mit der „slice”-Syntax. Das sollte eigentlich alles in einem Grundlagentutorial dran kommen. Ausser `index()` vielleicht. Die Methoden auf Zeichenketten, sollte man sich alle mal anschauen. Wie eigentlich die Methoden auf allen Grunddatentypen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Um mal alle Eventualitäten auszuschließen: Kannst Du uns mal eine Beispieldatei posten? Und: Weißt Du, ob es sich um ein bekanntes Format handelt? Und: Kannst Du ausschließen, dass es sich um JSON handelt?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
kimaro
User
Beiträge: 5
Registriert: Donnerstag 9. Oktober 2014, 09:16

Hi,

erstmal vielen Dank für die Antworten.
Habe jetzt schon mehrere Versuche unternommen diese Stelle zu suchen und überhaupt auszugeben.

Code: Alles auswählen

while a == 1:
    c = c + 1
    try:
        o = pfad + cfg[c]
 #       print(o)              
        if 'define host{' in open(o).read():
            for zeile in open(o).read():
                m = re.match(r"define host (})", zeile)
                if m != None:
                    print(m.group ())
                    print(zeile)
Dieser gibt mir jedoch gar nichts aus.


Tut mir leid bin echt noch ein blutiger Anfänger
BlackJack

@kimaro: Das sieht alles ein wenig unkoordiniert und geraten aus. Teil das Problem in kleinere Teile auf und versuche nicht alles in einem grossen Code-Klumpen zu lösen. Schreibe zum Beispiel eine Funktion die *eine* Datei verarbeitet. Erst wenn die funktioniert eine die mehrere Dateien verarbeitet in dem sie die funktion die eine Datei verarbeitet für jeden Dateinamen aufruft.

Gewöhn dir auch gleich diese einbuchstabigen Namen ab. Namen sollten dem Benutzer vermitteln was der Wert bedeutet. Kein Mensch rät auf anhieb das `o` ein Pfad zu einer Konfigurationsdatei ist. Einbuchstabige Namen sind okay für die üblichen Laufvariablen aus der Mathematik, also beispielsweise `i` und `j`, wenn man da nur ganze Zahlen dran bindet. Oder in sehr beschränkten Gültigkeitsbereichen wie „list comprehensions” (LCs), Generatorausdrücken, oder ``lambda``-Definitionen. Ansonsten sollte man in Worten ausschreiben was gemeint ist. Zum Beispiel `path` statt `o`.

Das ``while a == 1:`` würde ich zu einem ``while True:`` machen und die Schleife falls nötig mit einem ``break`` verlassen. Ich gehe jetzt dabei davon aus das `a` nur die Werte 0 und 1 annimmt. Für solche Flags sollte man auch besser `True` und `False` verwenden.

Da `c` hochgezählt wird, kann man auch gleich eine ``for``-Schleife verwenden mit `c` als Laufvariable. Und da `c` als Index in `cfg` verwendet wird, vermute ich mal ganz stark, dass es für `c` auch eine Obergrenze gibt. Und ich hoffe das ``try`` ist nicht dazu da um diesen Fall abzudecken. Denn dann bräuchte man `c` überhaupt gar nicht, denn man kann in Python direkt über die Elemente von Containerobjekten iterieren, ohne einen Umweg über einen Index.

Pfadteile setzt man mit `os.path.join()` zusammen.

Dateien die man öffnet sollte man auch wieder schliessen. Am besten verwendet man ein `open()` immer zusammen mit einer ``with``-Anweisung.

Du liest die Datei für das ``if`` komplett in den Speicher, suchst nach der Teilzeichenkette und verwirfst die eingelesenen Daten, nur um in der nächsten Zeile die selbe Datei wieder komplett in den Speicher zu lesen. Das ist nicht wirklich effizient.

`zeile` ist ein falscher Name für etwas was keine Zeile darstellt. Schau Dir mal die Werte an, die an diesen Namen gebunden werden.

Zumindest für den regulären Ausdruck den Du da verwendest braucht man keine regulären ausdrücke. *Das* hättest Du auch wieder mit dem ``in``-Operator prüfen können. Allerdings habe ich den verdacht, dass das nicht das ist was geprüft werden soll an der Stelle.

Aber bevor wir weiter raten wie die Dateien tatsächlich aussehen, geh doch mal bitte auf die Fragen von Hyperion ein.
kimaro
User
Beiträge: 5
Registriert: Donnerstag 9. Oktober 2014, 09:16

Hi,
ja da habe ich noch ein ganzes stück Arbeit vor mir :shock:

So habe auf die Schnelle kurze verbesserungen durchgeführt, werde mich nach der
Mittagspause nochmal richtig ran setzten um den Mist fertig zu machen.

Hin und wieder nehme ich einen Print mit einem völlig Sinnlosen Wort drin, nur um zu sehen
ob der überhaupt den kram den er ausführen soll versucht ;)

Code: Alles auswählen

import os
import re

pfad = '/usr/local/nagios/etc/objects/hosts/'


counter = 0
cfg =[]
for x in os.listdir(pfad):
    if x.split(".")[1] =="cfg":
        cfg.append(x)


c = 0
a = 1
while a == 1:
    c = c + 1
    try:
        pfad2 = pfad + cfg[c]
 #       print(o)              
        if 'define host{' in open(pfad2).read():
            for zeile in open(pfad2).read():
                m = re.match(r"define host (})", zeile)
                if m != None:
                    print(m.group ())
                    print(zeile)
   
        except:
            print("neu")
            a = 0


Dies ist der aktuelle Code "Brocken" :?

Code: Alles auswählen

 define host{
    host_name              UbuntuNagios
    alias                  UbuntuNagios
    check_command          check-host-alive
    parents                Server 6
    address                127.0.0.1
    notification_options   d,u,r
    max_check_attempts     5
    name                   UbuntuNagios
}


#define service{
#       host_nam1e              UbuntuNagios
#       service_description     check-disk-sda1
#       check_command           check_local_disk!20%!10%! /
#       max_check_attempts      5
#       check_interval          5
#       retry_interval          3
#       check_period            24x7
#}


#define service{
#       host_nam1e              UbuntuNagios
#       service_description     check-ping
#       check_command           check_ping!100.0,20%!500.0,60%
#       max_check_attempts              5
#       check_interval          1
#       retry_interval          3
"host.cfg" [Nur Lesen] 33L, 718C    

Dies ist eine der besagten .cfg Datein
Zur info: Das Nagios ist ein Testsystem nicht das Produktive :D

Vielen Dank schonmal im Vorraus
BlackJack

@kimaro: Da würde ich mir ja als erstes mal http://pynag.org/ anschauen.
kimaro
User
Beiträge: 5
Registriert: Donnerstag 9. Oktober 2014, 09:16

Das ist sehr gut gemeint aber Problem an der sache ist, dass ich auch dabei was lernen möchte ;)
wenn ich sowas ähnliches für ein nicht Nagios system mache dann fange ich wieder bei Null an :/
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@kimaro: mit einfachen String-Vergleichen wirst Du nicht glücklich werden. Du mußt einen kompletten Parser für dieses Dateiformat schreiben. Das heißt, Dir die Spezifikation besorgen und in Python umsetzen.
BlackJack

@kimaro: Ich denke schon das man erst nach einer passenden Bibliothek sucht die das sicher besser macht als ein selbstgeschriebener, kruder Ad-Hoc ”Parser” ist schon eine lernenswerte Lektion.

Es ist nicht so einfach sich etwas robustes selber zu schreiben was dann auch tatsächlich mit allen möglichen und eigentlich gültigen Nagios-Konfigurationsdateien auch tatsächlich klar kommt.

Noch mal zum letzten Quelltext: Ob eine Zeichenkette mit einer bestimmten Zeichenkette endet kann man einfacher mit der `endswith()`-Methode testen. Die ganze Schleife liesse sich aber mit dem `glob`-Modul vermeiden.

Der Code ignoriert das erste Element in `cfg`. Da `os.listdir()` keine bestimmte Reihenfolge garantiert ignoriert man da also einen zufälligen Dateinamen. Das ist sicher nicht so gewollt. Und wie gesagt, man kann in Python *direkt* über die Elemente von Listen iterieren, ohne einen Umweg über einen Index.

Wenn Du das unbedingt selber schreiben wollst, dann müsstest Du systematisch beschreiben wie Du an den oder die gewünschten Werte aus so einer Datei heran kommst. Und als erstes vielleicht entscheiden ob Du die Datei nun komplett einlesen und als ganzes betrachten möchtest, oder die Zeilen der Datei einzeln. Der Mix den Du da versuchst funktioniert nicht.

Man könnte sich zum Beispiel die Zeilen der Reihe nach anschauen und sich merken ob man sich gerade in einem Host-Abschnitt befindet oder nicht. Und solange man in einem Abschnitt ist, die Zeile einer Ergebnisliste hinzufügen, und ansonsten ignorieren. Ungetestet:

Code: Alles auswählen

import os
from glob import iglob
from pprint import pprint


def extract_object(object_type, lines):
    in_object = False
    result = list()
    start_marker = 'define {0}{{'.format(object_type)
    for line in lines:
        if line.startswith(start_marker):
            in_object = True
        if in_object:
            result.append(line)
        if line.lstrip().startswith('}'):
            in_object = False
    return result


def main():
    base_path = '/usr/local/nagios/etc/objects/hosts/'
    for filename in iglob(os.path.join(base_path, '*.cfg')):
        with open(filename) as lines:
            print('>', filename)
            pprint(extract_object('host', lines))


if __name__ == '__main__':
    main()
kimaro
User
Beiträge: 5
Registriert: Donnerstag 9. Oktober 2014, 09:16

kann ich nicht einfach von der ersten "{" bis "}" etwas ausschneiden?
also die erste } im text suchen davon die Zeile ausgeben lassen und dann eben von Zeile 1 bis zur gesuchten Zeile den kram ausschneiden?

danke schon mal für die ausführliche Antwort
BlackJack

@kimaro: Jain, dann könntest Du auch versuchen einen regulären Ausdruck zu schreiben der meiner Funktion entspricht. Lesbarer und verständlicher wird es dadurch aber wohl nicht.

Zumal Du nicht einfach nach dem ersten '}' nach dem '{' suchen darfst, denn *innerhalb* eines Blocks kann das ja auch beliebig oft vorkommen. Du musst schon genau das '}' finden welches auch das Gegenstück zum '{' vom 'define host' ist. Und Du musst natürlich auch sicherstellen, dass das '{' zu einem 'define host' gehört *und* dass dieses nicht auskommentiert ist.

Sachen die für Menschen sehr einfach aussehen, können manchmal überraschend kompliziert formal zu beschreiben sein, so dass auch ein Rechner damit klar kommt.
Antworten