frazman
frazman

Reputation: 33223

Capturing *all* terminal output of a program called from Python

I have a program which can be execute as

./install.sh

This install bunch of stuff and has quite a lot of activity happening on screen..

Now, I am trying to execute it via

p = subprocess.Popen(executable, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()

With the hope that all the activity happening on the screen is captured in out (or err). However, content is printed directly to the terminal while the process is running, and not captured into out or err, which are both empty after the process is run.

What could be happening here? How can this content be captured?

Upvotes: 0

Views: 989

Answers (1)

Charles Duffy
Charles Duffy

Reputation: 295373

In general, what you're doing is already sufficient to channel all output to your variables.

One exception to that is if the program you're running is using /dev/tty to connect directly to its controlling terminal, and emitting output through that terminal rather than through stdout (FD 1) and stderr (FD 2). This is commonly done for security-sensitive IO such as password prompts, but rarely seen otherwise.


As a demonstration that this works, you can copy-and-paste the following into a Python shell exactly as given:

import subprocess
executable = ['/bin/sh', '-c', 'echo stdout; echo stderr >&2']
p = subprocess.Popen(executable, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print "---"
print "output: ", out
print "stderr: ", err

...by contrast, for a demonstration of the case that doesn't work:

import subprocess
executable = ['/bin/sh', '-c', 'echo uncapturable >/dev/tty']
p = subprocess.Popen(executable, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print "---"
print "output: ", out

In this case, content is written to the TTY directly, not to stdout or stderr. This content cannot be captured without using a program (such as script or expect) that provides a fake TTY. So, to use script:

import subprocess
executable = ['script', '-q', '/dev/null',
              '/bin/sh', '-c', 'echo uncapturable >/dev/tty']
p = subprocess.Popen(executable, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
print "---"
print "output: ", out

Upvotes: 7

Related Questions