Rückgabe von lsdvd in Python einlesen

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
Marc_4
User
Beiträge: 6
Registriert: Samstag 9. Februar 2013, 16:58

Hallo,

ich bin gerade dabei mich in Python einzuarbeiten und neu hier im Forum.
Mittels des kleinen Tools lsdvd (http://wiki.ubuntuusers.de/lsdvd) kann man Infos zu einer eingelegten DVD auslesen.
Dabei gibt es auch die Möglichkeit mit dem Befehl:
lsdvd Pfad -Oy
das ganze als Python-Dictionary formatiert auszugeben. Meine Frage ist jetzt, wie ich an diese Werte tatsächlich herankomme?
Wenn ich den Befehl mit os.system aufrufe erhalte ich nur eine 0 als Rückgabewert, oder habe ich da was übersehn?
Muss ich die Ausgabe erst in eine Datei schreiben, und die dann wieder einlesen?

Würde mich über eure Hilfe freuen!
BlackJack

@Marc_4: Externe Programme sollte man mit dem `subprocess`-Modul starten.
Marc_4
User
Beiträge: 6
Registriert: Samstag 9. Februar 2013, 16:58

Hallo BlackJack,

danke für den Tip mit subprocess!
Habe es grad mal probiert (angelehnt an ein Beispiel im Netz):

Code: Alles auswählen

DVD = subprocess.Popen("lsdvd -Oy" Pfad, shell=True, stdout=subprocess.PIPE)
Info = DVD.communicate()[0]
Der Inhalt von Info ist dann allerdings auch nur ein String. Irgendwie steh ich auf dem Schlauch wie ich auf die Ausgabe als dictionary zugreifen kann?
BlackJack

@Marc_4: Als erstes solltest Du das mal ohne unnötige Shell aufrufen.

Mit `ast.literal_eval()` kann man die Zeichenkette *sicher* in eine Python-Datenstruktur überführen.
Marc_4
User
Beiträge: 6
Registriert: Samstag 9. Februar 2013, 16:58

Hallo BlackJack,

wenn ich es ohne Shell aufrufe erhalte ich eine Fehlermeldung:
OSError: [Errno 2] No such file or directory: 'lsdvd -Oy /media/Multimedia/Laptop/BB/1-1'
Obwohl das Verzeichnis existiert.
Ich glaub langsam mein Vorhaben ist etwas zu komplex für meine noch bescheidenen Kenntnisse...
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Wenn man dir einen Hinweis auf eine Funktion gibt, dann solltest du diese nicht einfach blind benutzen, sondern einen Blick in die Dokumentation riskieren ;-) Der erste Parameter von Popen sollte eine Liste von Argumenten sein und kein String:

Code: Alles auswählen

["lsdvd",  "-Oy",  "/media/Multimedia/Laptop/BB/1-1"]
Nun kannst du denn shell-Parameter weglassen, da die Shell nichts mehr interpretieren muss.
Das Leben ist wie ein Tennisball.
Marc_4
User
Beiträge: 6
Registriert: Samstag 9. Februar 2013, 16:58

Hallo & Danke!

Konnte das Problem jetzt soweit lösen.
Hab ich das in der Doku zu subprocess richtig verstanden, das man in der Liste mit übergebenen Argumenten auch die jeweilige Option und ihr zugehöriges Argument als einzelnes Listenobjekt übergibt? Also in der Form:

Code: Alles auswählen

["avconv",  "-i", "Input.flv", "-vcodec", "libx264"]


Meine Lösung für das Einlesen der Track-Anzahl auf der jeweiligen DVD sieht jetzt so aus, für etwaige Kritik/Anregungen wäre ich dankbar!

Code: Alles auswählen

RawDvdList = os.listdir(RawDvdPath)
for DvdNumber, DvdName in enumerate(RawDvdList):
    DvdInfo = subprocess.Popen(["lsdvd",  "-Oy", "/home/user/DVDs/" + DvdName], stdout=subprocess.PIPE)
    # exec() erzeugt eine Dictionary-Variable mit dem Namen lsdvd
    exec(DvdInfo.communicate()[0])
    Tracks = len(lsdvd.get("track"))
    print(Tracks)
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

@Marc_4: warum gibst Du im os.listdir das Verzeichnis über RawDvdPath an und beim Subprocessaufruf direkt als "/home/user/DVDs/"?

Der direkte Aufruf von exec mit Strings aus externen Quellen ist ein Scheunentor großes Sicherheitsrisiko. Warum benutzt Du nicht, wie von Blackjack vorgeschlagen ast.literal_eval?
BlackJack

@Marc_4: Das ``exec`` ist gruselig. Da wird beliebiger Code der von einem externen Programm kommt einfach so ausgeführt. Und das obwohl man eigentlich nur ganz einfache Daten haben möchte, also etwas was mit deutlich weniger Sicherheitsrisiko passieren kann und sollte. Ich würde da entweder das 'lsdvd =' weglassen und den Rest *sicher* mit der Funktion aus dem `ast`-Modul parsen oder eine andere Ausgabe bei dem externen Werkzeug wählen, zum Beispiel XML.

Die Namensgebung weicht vom Style Guide for Python Code ab.

`DvdNumber` wird gar nicht verwendet.

Statt ``lsdvd.get('track')`` hätte ich ``lsdvd['track']`` geschrieben. Falls dieser Schlüssel nicht existiert, ist die Fehlermeldung eindeutiger.

`Tracks` hätte ich `track_count` genannt, denn der Name steht ja nur für die Anzahl der Tracks und nicht für die Tracks selber.

Es gibt einmal `RawDvdPath` und dann den literalen Wert '/home/user/DVDs/' wo ich mal vermute, dass die identisch sind!? Pfade sollte man mit `os.path.join()` und nicht mit ``+`` zusammensetzen.
Marc_4
User
Beiträge: 6
Registriert: Samstag 9. Februar 2013, 16:58

Hallo,

danke für eure Anregungen!
Den Style Guide werde ich mir bei Gelegenheit mal zu Gemüte führen, im Moment fehlt mir da einfach die Zeit.
DvdNumber soll noch eine Verwendung finden, hätte aber aus dem Beispiel rausgekonnt.
Das mit dem Pfad war mir gar nicht aufgefallen, ich kann aber wirklich in beiden Fällen die Variable nutzen und os.path.join() ist auch sehr nützlich.

Hab jetzt soweit alles angepasst, allerdings funktioniert es mit ast.literal_eval nicht, ich erhalte folgende Fehlermeldung:

ValueError: malformed node or string: b"{\n 'device' : '/media/Multimedia/Laptop/BB/1-1',\n 'title' : 'unknown',\n 'vmg_id' : 'DVDVIDEO-VMG',\n 'provider_id' : '',\n 'track' : [\n {\n 'ix' : 1,\n 'length' : 3344.130,\n 'vts_id' : 'DVDVIDEO-VTS',\n },\n {\n 'ix' : 2,\n 'length' : 2775.130,\n 'vts_id' : 'DVDVIDEO-VTS',\n },\n {\n 'ix' : 3,\n 'length' : 2774.020,\n 'vts_id' : 'DVDVIDEO-VTS',\n },\n ],\n 'longest_track' : 1,\n}\n"

Kann es sein das er mit den Steuerzeichen der Zeilenumbrüche Probleme hat?
BlackJack

@Marc_4: Nein, die Funktion will eine Zeichenkette haben, sagt die Fehlermeldung ja auch, und Du übergibst ein Objekt vom Typ `bytes`. Das musst Du dekodieren.
Volker
User
Beiträge: 1
Registriert: Sonntag 17. Februar 2013, 10:49

Code: Alles auswählen

   
    import ast

    source = "/dev/sr0"
    DvdInfo = subprocess.Popen(["lsdvd",  "-Oy", source], stdout=subprocess.PIPE).communicate()[0]
    dicString = '='.join(DvdInfo.split('=')[1:]).strip()
    # oder einfacher, aber festverdrahtet:
    #dicString = DvdInfo[8:]
    dvdDic = ast.literal_eval(dicString)
    print "Titel:", dvdDic["title"]
Die einfachere Version

Code: Alles auswählen

dicString = DvdInfo[8:]
hat den Nachteil, daß
sie fehlschlägt, wenn die Ausgabe von lsdvd nicht

Code: Alles auswählen

lsdvd = {...}
sondern

Code: Alles auswählen

ld = {...}
oder so ist, mit der gleichen Struktur, aber anderem Variablennamen.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Code: Alles auswählen

dicString = DvdInfo.split('=',1)[1]
Antworten