Reputation: 1374
I just realized that if I use check=True in a subprocess, I don't get the CompletedProcess
descriptor. This looks like a design flaw : How can I get the error messages sent by the called program ?
Obviously there are other ways to get the return code of my program, but the point here is that the check=True parameter seems incompatible with error handling.
Or did I miss something ?
#!/usr/bin/python3
import subprocess
import sys
subproc1 = subprocess.run('ls /nonexistent', stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True)
print(subproc1.stderr, file=sys.stderr, end='', flush=True)
print(subproc1.stdout, file=sys.stdout, end='', flush=True)
print('▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔', file=sys.stderr, flush=True)
try: subproc2 = subprocess.run('ls /', stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=True)
except subprocess.CalledProcessError:
print("FAIL")
print(subproc2.stderr, file=sys.stderr, end='', flush=True)
print(subproc2.stdout, file=sys.stdout, end='', flush=True)
print('▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔', file=sys.stderr, flush=True)
raise
else:
print("GOOD")
print(subproc2.stderr, file=sys.stderr, end='', flush=True)
print(subproc2.stdout, file=sys.stdout, end='', flush=True)
print('▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔', file=sys.stderr, flush=True)
try: subproc3 = subprocess.run('ls /nonexistent', stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=True)
except subprocess.CalledProcessError:
print("FAIL")
print(subproc3.stderr, file=sys.stderr, end='', flush=True)
print(subproc3.stdout, file=sys.stdout, end='', flush=True)
print('▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔', file=sys.stderr, flush=True)
raise
else:
print("GOOD")
print(subproc3.stderr, file=sys.stderr, end='', flush=True)
print(subproc3.stdout, file=sys.stdout, end='', flush=True)
print('▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔', file=sys.stderr, flush=True)
yields:
ls: cannot access '/nonexistent': No such file or directory
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
GOOD
bin
[...]
var
vmlinuz
vmlinuz.old
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
FAIL
Traceback (most recent call last):
File "/root/subprocess_error.py", line 27, in <module>
try: subproc3 = subprocess.run('ls /nonexistent', stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=True)
File "/usr/lib/python3.7/subprocess.py", line 487, in run
output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'ls /nonexistent' returned non-zero exit status 2.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/root/subprocess_error.py", line 30, in <module>
print(subproc3.stderr, file=sys.stderr, end='', flush=True)
NameError: name 'subproc3' is not defined
Upvotes: 0
Views: 364
Reputation: 295281
The captured content is saved to the exception itself.
Assign the exception object to a variable, and read .stdout
and .stderr
off it.
import subprocess
import sys
try:
subproc3 = subprocess.run('ls /nonexistent', stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, check=True)
except subprocess.CalledProcessError as ex:
print("FAIL")
print(ex.stderr, file=sys.stderr, end='', flush=True)
print(ex.stdout, file=sys.stdout, end='', flush=True)
Upvotes: 1