15

I would like to run a command using subprocess.run() and then get its stdout/stderr as a string, but I want the subprocess to also print its output to the console normally while running. If I do

result = subprocess.run(['ls', '-al'])

then I can see the output printed to my console but I can't access the output after the command runs. If I do

result = subprocess.run(['ls', '-al'], capture_output=True, text=True)

I can access result.stdout and result.stderr but nothing is printed to the console while the command is running. Can I have both printing to the console and saving to result.stdout?

Boris Verkhovskiy
  • 14,854
  • 11
  • 100
  • 103
  • 2
    Can you use `subprocess.Popen()` instead? Maybe [this](https://stackoverflow.com/questions/4417546/constantly-print-subprocess-output-while-process-is-running) can help? – MatBBastos Jun 04 '21 at 20:33

1 Answers1

4

From the documentation of subprocess.run :

Run the command described by args. Wait for command to complete, then return a CompletedProcess instance.
[...]
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 docs for subprocess.PIPE say :

Special value that can be used as the stdin, stdout or stderr argument to Popen and indicates that a pipe to the standard stream should be opened. Most useful with Popen.communicate().

The doc for the Popen constructor parameter stdout :

stdin, stdout and stderr specify the executed program’s standard input, standard output and standard error file handles, respectively. Valid values are PIPE, DEVNULL, an existing file descriptor (a positive integer), an existing file object, and None.

So using capture_output=True is a no-go, because the output will be stored in a pipe for you to read after the call finishes.

The simpler is for you to use subprocess.Popen as @MatBBastos suggested, with wich you can communicate (repeatedly sending content to stdin and receiving content from stdout/stderr). The solution linked is a bit dated (cf its own comments; Python 2) but should work well. A related solution is this one.

To keep using subprocess.run, you will have to provide a file descriptor as stdout parameter, which I don't know how would have to redirect to a file object that does what you want : writing to the standard stream, but also keeping a copy in memory for later use.
There are docs in the io module, and a lot of questions on Stack Overflow about doing things like that, but it is notably more difficult than the other way.

Lenormju
  • 4,078
  • 2
  • 8
  • 22
  • While it is possible in theory to implement a file object that would both write its content to stdout and keep a copy for later, it seems difficult ([cf](https://stackoverflow.com/questions/19409025/python-stringio-doesnt-work-as-file-with-subrpocess-call) and [cf](https://stackoverflow.com/questions/52340974/possible-to-get-a-file-descriptor-for-pythons-stringio)). It remints a bit of what [`tee`](https://en.wikipedia.org/wiki/Tee_(command)) does, but I still don't know how to implement that. Using POpen.Communicate is still the way to go, althought cumbersome. – Lenormju Aug 17 '23 at 12:41