Reputation: 4185
I want to allow a user of my program to specify whether the output is supposed to be verbose or not. I.e., that run subprocesses are printing all the output they generate or not.
I used to be able to do:
out = subprocess.DEVNULL
if verbose:
out = subprocess.STDOUT
subprocess.run(['ls','-la'],stderr=out,stdout=out)
But this results in this error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/subprocess.py", line 489, in run
with Popen(*popenargs, **kwargs) as process:
File "/usr/lib/python3.8/subprocess.py", line 854, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/usr/lib/python3.8/subprocess.py", line 1702, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 9] Bad file descriptor
I was able to narrow down the error to:
subprocess.run(['ls','-la'],stdout=out)
I.e., defining that the output is supposed to go to stdout is leading to this issue. This was not the case in earlier (i.e., < 3.8) Python.
Is this by design and how can I still achieve the verbose/non-verbose distinction without doubling the subprocess.run calls?
Upvotes: 3
Views: 1651
Reputation: 779
You can use the capture_output
option :
If capture_output is true, stdout and stderr will be captured. When used, the internal Popen object is automatically created with stdout=PIPE and stderr=PIPE. The stdout and stderr arguments may not be supplied at the same time as capture_output. If you wish to capture and combine both streams into one, use stdout=PIPE and stderr=STDOUT instead of capture_output.
subprocess.run(['ls','-la'], capture_output=not verbose)
The stdout argument takes None, subprocess.PIPE, a file object or a file descriptor but not subprocess.STD_OUT
which is just a special value that indicates that stderr should go to stdout.
You can also pass subprocess.DEVNULL
to stdout
and subprocess.STDOUT
to stderr
if you want the subprocess to print nothing and to keep nothing in memory.
subprocess.run(
["ls", "-la"], stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT
)
For the verbose mode the default value, which is None for both arguments, will do. You can also pass sys.stdout
to stdout
and sys.stderr
to stderr
. All those are equivalent :
subprocess.run(
["ls", "-la"]
)
subprocess.run(
["ls", "-la"], stdout=None, stderr=None
)
subprocess.run(
["ls", "-la"], stdout=sys.stdout, stderr=sys.stderr
)
Upvotes: 4