Wie handhabt man Fehler und stderr beim Pipelinen mehrerer Shell-Kommandos mittels subprocess in Python 3?

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
AFoeee
User
Beiträge: 11
Registriert: Montag 22. Januar 2018, 07:00

Hallo,
meine Frage bezieht sich auf das Handhaben von Fehlern und stderr beim Ausführen von mehreren Shell-Kommandos mittels des Moduls "subprocess".

Zu meinem speziellen Fall:
Ich möchte zwei Shell-Kommandos ausführen. Dabei sollen die Kommandos per Pipelining miteinander verbunden werden.
Welche Klassen, Funktionen, etc. aus "subprocess" verwendet werden, spielt eine untergeordnete Rolle. Ich möchte allerdings, dass
  • Stdout ausgelesen werden kann. (das finale Ergebnis)
  • dieses Pipelining über Python stattfindet. (also ohne "shell=True" setzen zu müssen)
Die reine Funktionalität sieht so aus:

Code: Alles auswählen

import subprocess
import shlex

# Die Kommandos entsammen ImageMagick
cmd1 = shlex.split("convert -density 300 \"{}\" :-".format(input("> "))
cmd2 = shlex.split("identify -format %wx%h -")

sp = subprocess.Popen(cmd1, stdout=subprocess.PIPE)
result = subprocess.check_output(cmd2, stdin=sp.stdout)
Wie aber
  • stelle ich fest ob während der Ausführung irgendeines Kommandos ein Fehler aufgetreten ist?
  • handhabe ich stderr der einzelnen Kommandos? (z.B. zum Umlenken in eine Log-Datei)

Zu meinem (etwas halbherzigen) Versuch:
In der Dokumentation von subprocess.check_output() steht:
The stdout argument is not allowed as it is used internally.
To capture standard error in the result, use stderr=STDOUT.
Daher hatte ich folgendes versucht:

Code: Alles auswählen

try:
    sp = subprocess.Popen(cmd1, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    result = subprocess.check_output(cmd2, stdin=sp.stdout, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as error:
    log(HOW_DO_I_ACCESS_STDOUT?)
finally:
    if sp.returncode != 0:
        log(sp.stderr.read())
    sp.stdout.close()
Der Code hat folgende Fehler:
  • Wenn ein "CalledProcessError" auftritt, ist die Variable "result" (in der stdout stehen sollte) nicht definiert. In eben dieser Variablen sollte aber auch stderr enthalten sein, da nach stdout umgelenkt wurde.
  • Stdout wird von "subprocess.check_output()" intern verwendet, also darf ich es nicht angeben. Ohne es anzugeben kann ich aber nur über "result" darauf zugreifen?
  • sp.stderr.read() wirft auch einen Fehler, weil "sp.stderr" NoneType hat.
Mit freundlichen Grüßen
AFoeee
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@AFoeee: `subprocess` benutzt man gerade dazu, um nicht irgendetwas von der Shell parsen zu lassen, da ist es kontraproduktiv, shlex zu benutzen:

Korrekt:

Code: Alles auswählen

import subprocess

# Die Kommandos entstammen ImageMagick
input_filename = input("> ")
sp = subprocess.Popen(["convert", "-density", "300", input_filename, ":-"], stdout=subprocess.PIPE)
result = subprocess.check_output(["identify", "-format", "%wx%h", "-"], stdin=sp.stdout)
Wenn Du stderr einfach in eine log-Datei umleiten willst, gib das Filehandle jedem Aufruf als stderr=logfile an. CalledProcessError hat das Attribut `output`.
AFoeee
User
Beiträge: 11
Registriert: Montag 22. Januar 2018, 07:00

@Sirius3: Vielen Dank, das hat mein Problem gelöst!

Ich hatte keine Ahnung, dass man stderr auch gleich eine Log-Datei übergeben kann. :shock:

shlex hab ich verwendet, weil ich folgendes in der Python Doku gelesen hatte:
shlex.split() can be useful when determining the correct tokenization for args, especially in complex cases:

Code: Alles auswählen

>>> import shlex, subprocess
>>> command_line = input()
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
>>> args = shlex.split(command_line)
>>> print(args)
['/bin/vikings', '-input', 'eggs.txt', '-output', 'spam spam.txt', '-cmd', "echo '$MONEY'"]
>>> p = subprocess.Popen(args) # Success!
Note in particular that options (such as -input) and arguments (such as eggs.txt) that are separated by whitespace in the shell go in separate list elements, while arguments that need quoting or backslash escaping when used in the shell (such as filenames containing spaces or the echo command shown above) are single list elements.
Mit freundlichen Grüßen
AFoeee
Antworten