python - Obtenga el código de salida y stderr de la llamada de subproceso
python-3.x (4)
Leí las funciones provistas por el subproceso - llamada, marca de verificación, verificación de salida, y entiendo cómo funciona cada uno y difiere en funcionalidad uno del otro. Actualmente estoy usando check_output, así que puedo tener acceso a stdout, y usé "try block" para atrapar la excepción, de la siguiente manera:
# "cmnd" is a string that contains the command along with it''s arguments.
try:
cmnd_output = check_output(cmnd, stderr=STDOUT, shell=True, timeout=3, universal_newlines=True);
except CalledProcessError:
print("Status : FAIL")
print("Output: /n{}/n".format(cmnd_output))
El problema al que me estoy enfrentando es cuando se lanza una excepción, "cmnd_output" no se inicializa y no tiene acceso a stderr, y aparece el siguiente mensaje de error:
print("Output: /n{}/n".format(cmnd_output))
UnboundLocalError: local variable ''cmnd_output'' referenced before assignment
Creo que eso se debe a que la excepción hace que el "check_output" se resguarde inmediatamente sin ningún procesamiento posterior, también conocido como asignación a "cmnd_output", en el bloque try. Por favor, corríjame si estoy equivocado.
¿Hay alguna manera de que pueda obtener acceso a stderr (está bien si se envía a stoutr) y tener acceso al código de salida? Puedo verificar manualmente si se aprueban o no según el código de salida sin que se produzca la excepción.
Gracias, Ahmed.
¿por qué no inicializar el varible cmnd_output antes de la instrucción try? De esa forma funcionará de la manera que usted espera. La siguiente línea funcionaría, solo agréguela encima de la declaración try:
cmnd_output = ''''
Ambas soluciones propuestas mezclan stdout / stderr o usan Popen
que no es tan fácil de usar como check_output
. Sin embargo, puede lograr lo mismo y mantener stdout / stderr por separado, mientras usa check_output
si simplemente captura stderr usando una tubería:
import sys
import subprocess
try:
subprocess.check_output(cmnd, stderr=subprocess.PIPE)
except subprocess.CalledProcessError as e:
print(''exit code: {}''.format(e.returncode))
print(''stdout: {}''.format(e.output.decode(sys.getfilesystemencoding())))
print(''stderr: {}''.format(e.stderr.decode(sys.getfilesystemencoding())))
En este ejemplo, dado que capturamos stderr, está disponible en el atributo stderr
la excepción (sin capturar con el conducto, simplemente sería None
).
La solución aceptada cubre el caso en el que está bien mezclar stdout
y stderr
, pero en los casos en que el proceso hijo (por cualquier razón) decide usar stderr
ADEMÁS de stdout
para un resultado no fallado (es decir, para generar un resultado no crítico advertencia), entonces la solución dada puede no ser deseable.
Por ejemplo, si va a hacer un procesamiento adicional en la salida, como convertir a JSON, y mezcla en el stderr
, entonces el proceso general fallará ya que el resultado no será JSON puro debido a la salida de stderr
agregada.
He encontrado lo siguiente para trabajar en ese caso:
cmd_args = ... what you want to execute ...
pipes = subprocess.Popen(cmnd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
std_out, std_err = pipes.communicate()
if pipes.returncode != 0:
# an error happened!
err_msg = "%s. Code: %s" % (std_err.strip(), pipes.returncode)
raise Exception(err_msg)
elif len(std_err):
# return code is 0 (no error), but we may want to
# do something with the info on std_err
# i.e. logger.warning(std_err)
# do whatever you want with std_out
# i.e. json.loads(std_out)
Prueba esta versión:
import subprocess
try:
output = subprocess.check_output(
cmnd, stderr=subprocess.STDOUT, shell=True, timeout=3,
universal_newlines=True)
except subprocess.CalledProcessError as exc:
print("Status : FAIL", exc.returncode, exc.output)
else:
print("Output: /n{}/n".format(output))
De esta forma, imprimirá la salida solo si la llamada fue exitosa. En el caso de un CalledProcessError
imprime el código de retorno y la salida.