Frage zu "subprocess"

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
heyJo
User
Beiträge: 23
Registriert: Mittwoch 24. Januar 2018, 20:49
Wohnort: Köln

Hallo zusammen,

ich komme einfach nicht weiter und brauche mal einen Tipp.

In einem Verzeichnis habe ich viele Bilder, die ich zu einem Video zusammenfügen will.

Auf der Konsole (bash) klappt das mittels

Code: Alles auswählen

mencoder mf://*.jpg -mf fps=15 -o output.avi -ovc lavc -lavcopts vcodec=mpeg4
tadellos. Nach einigen Minuten ist das Video fertig.

Nun will ich diesen Befehl in Python "einbinden".

In der Python Shell habe ich folgendes eingegeben:

Code: Alles auswählen

import subprocess

vi = subprocess.Popen(["mencoder","mf://*.jpg", "-mf fps=15","-o output.avi", "-ovc lavc", "-lavcopts", "vcodec=mpeg4"])
<subprocess.Popen object at 0x751cd4b0>
vi.poll()
1
Die "1" zeigt mir ja an, dass ein Fehler passiert ist.
Wo liegt mein Fehler?

Gruß
Jo
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Dein Fehler liegt beim Aufteilen der Argumente. Das hier beispielsweise: "-mf fps=15" sind eigentlich *zwei* Argumente. Die Shell teilt so etwas an Leerzeichen auf.

`vi` ist ein furchtbarer, unbenutzbarer Editor. $GOTT sein Dank gibt es `vim`. 🙂 Beides sind keine Namen für ein Objekt das einen Prozess repräsentiert. (Es sei denn der führt `vi`, den Editor aus.)

Falls kein bestimmter Grund für `Popen` besteht würde man auch eher die `run()`-Methode verwenden. Und da am besten ``check=True`` übergeben, damit es auffällt wenn das externe Programm einen Fehlercode meldet.

Auf der Konsole gibt das übrigens bei mir folgenden Text aus:

Code: Alles auswählen

MEncoder 1.3.0 (Debian), built with gcc-7 (C) 2000-2016 MPlayer Team  
-mf fps=15 is not an MEncoder option

Exiting... (error parsing command line)
Wenn Du den nicht siehst, solltest Du darüber nachdenken wie Du das Python-Programm startest und warum Du so etwas nicht zu sehen bekommst.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
heyJo
User
Beiträge: 23
Registriert: Mittwoch 24. Januar 2018, 20:49
Wohnort: Köln

Vielen Dank __blackjack__,

mit

Code: Alles auswählen

subprocess.run(["mencoder" , "mf://*.jpg" , "-mf" , "fps=15" , "-o" , "output.avi" , "-ovc" , "lavc" , "-lavcopts" , "vcodec=mpeg4"])
klappt es nun.

Mein "Denkfehler" war, dass ich glaubte, dass die zusammengehörigen mencoder-Parameter ("-o output.avi") auch nur einen Listenwert darstellen.

Gruß
Jo
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Wenn sich die Parameter nie ändern, kannst du auch gleich shlex.split verwenden:

Code: Alles auswählen

shlex.split("mencoder mf://*.jpg -mf fps=15 -o output.avi -ovc lavc -lavcopts vcodec=mpeg4")
['mencoder',
'mf://*.jpg',
'-mf',
'fps=15',
'-o',
'output.avi',
'-ovc',
'lavc',
'-lavcopts',
'vcodec=mpeg4']
Jetzt schreien gleich sicherlich welche auf: "halt potentielle Sicherheitslücke"
Nein in diesem Fall ist es keine, da die Argumente vom Nutzer nicht definiert werden.
Falls man doch auf die Idee kommt, kann man auch gleich so etwas machen:

Code: Alles auswählen

import shlex
import subprocess


while True:
    command = input("Master, please give me orders: ")
    try:
        subprocess.call(shlex.split(command))
    except FileNotFoundError:
        print("Sorry master, I don't have the power...")
Der führt einfach die Befehle aus, die du eingibst oder halt jemand anderes.
Wollte eigentlich gar nicht so tief darauf eingehen.

Ich meine es war sogar in diesem Forum, dass ich ermahnt worden bin, dass shlex.split gefährlich sei.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
heyJo
User
Beiträge: 23
Registriert: Mittwoch 24. Januar 2018, 20:49
Wohnort: Köln

@DeaD_EyE vielen Dank für den Tipp!
Ich finde es immer hilfreich, wenn man eine Frage von verschiedenen Seiten aus betrachtet.


@ __blackjack__
Auf der Konsole bekomme ich auch diese Ausgabe. Die von mir angeführte Ausgabe bekomme ich, wenn ich die die Befehle manuell in der IDLE-Python-Shell eingebe.

Was mich nun eher verwirrt:
Wann ist denn nun
- „subprocess.call“ (Lösung DeaD_EyE)
- „subprocess.run“ (Lösung __blackjack__)
- „subprocess.Popen“ (mein Lösungsversuch)
sinnvoll?

Die 3.8.2 Doku meint:
„The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.“
Demnach schein ja „subprocess.run“ i.d.R. sinnvoll zu sein, zumindest für alle „… use cases it can handle“ ;-)

Gruß
Jo
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

Ob Du einen Use Case hast, der nicht behandelt werden kann, merkst Du ja, wenn Du es nicht mit `run` machen kannst.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist eine Frage der Historie. Popen ist das "kann alles, ist aber kompliziert Ding" das gleich zu Beginn da war. Weil das aber fuer einfache Faelle zu viel des guten ist (insbesondere wenn man nur mal kurz ein Kommando absetzen will, und nicht andauernd mit einem Kindprozess kommunizieren), hat man call einfguehrt. Das ist aber ziemlich limitiert, und so kam dann irgendwann run hinzu, das einen guten Kompromiss zwischen dem kann-alles von Popen und call darstellt. Es ist eingentlich ziemlich nah an Popen, aber mit netteren Dingen wie zB "capture_output" als Argument, check um zu pruefen ob der Prozess mit einem Fehlercode abbricht, etc.

Wenn du kannst, benutz also run. Das ist die modernste Variante.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1021
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

  • run - blockiert solange, bis der Prozess beendet ist und liefert dann eine Instanz von CompletedProcess zurück. Was in der Instanz in Informationen enthalten ist, hängt von den Argumenten ab.
  • call - blockiert solange, bis der Prozess beendet ist und liefert dann einen int zurück, dass dem Exit-Code des Programms entspricht. Unter Linux ist es die Regel, dass ein Programm den Wert 0 zurückliefert, wenn alles in Ordnung war.
  • Popen - blockiert nicht, außer man verwendet die Methode wait oder communicate
Wenn du also in deinem Programm weitermachen willst, während der Prozess noch im Hintergrund läuft, musst du Popen verwenden.
Wenn du auf die Terminierung des gestarteten Programms warten möchtest, kannst du run verwenden.
Wenn du auf die Terminierung des gestarteten Programms warten möchtest und ausschließlich den Exit-Code, das Programms benötigst, kannst du call verwenden.
Willst du das Programm hingegen im Hintergrund laufen lassen und benötigst trotzdem irgendwann den Rückgabewert des Programms, nutzt du Popen und die Methode poll. Die Methode liefert None zurück, wenn das Programm noch läuft.

Dann ist noch anzumerken, dass es Unterschiede zwischen Windows und Linux gibt.

Ich hoffe, dass ich es so richtig wiedergeben habe. Also mit dem subprocess-Modul kann man richtig viel Zeit verbringen...
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Ich würde `call()` gar nicht mehr verwenden. Das ist noch da für Legacy-Code der das halt schon verwendet, aber in neuem Code macht es nicht mehr wirklich Sinn. ``call(…)`` wird zu ``run(…).returncode``.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
heyJo
User
Beiträge: 23
Registriert: Mittwoch 24. Januar 2018, 20:49
Wohnort: Köln

Ah, ich sehe schon, dass es eine etwas komplizierte Kiste ist.

Danke für eure Hinweise und Anmerkungen!

Für meine Aufgabe (Verzeichnisse durchlaufen und aus den Bildern eines Verzeichnisses je ein Video erzeugen) scheint „subprocess.run“ das Mittel der Wahl zu sein.

Wenn ich es richtig verstanden habe, könnte ich mit dem Parameter "check=True" prüfen ob mencoder seine Arbeit regulär beendet hat.
Ansonsten würde ich eine Ausnahmemeldung erhalten?

Bei der Standarteinstellung "check=False" würde ich keine Meldung erhalten, wenn mencoder nicht richtig beendet wird, oder?
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@heyJo: Mit ``check=True`` gibt es eine Ausnahme falls der Prozess etwas anderes als ”Erfolg” meldet. Ohne dieses Argument passiert in dem Fall einfach gar nichts. Darum macht das in der Regel Sinn dieses Argument zu übergeben, es sei denn es ist *wirklich* okay, wenn das eigene Programm nicht mitbekommt wenn das externe Programm nicht erfolgreich war.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten