Skip to content
shellhacks.dev
Python

Run a Shell Command and Capture Output in Python

Run a command and read its output with subprocess.run(capture_output=True, text=True). check=True raises on a non-zero exit, and shell=True is needed for pipes.

2 min read
Summarize in AI

A script has to run another program and use what it prints.

Python
import subprocess

r = subprocess.run(["ls", "/nonexistent"], capture_output=True, text=True)
print(r.returncode, repr(r.stdout), r.stderr.strip())

r = subprocess.run(["echo", "héllo"], capture_output=True, text=True, check=True)
print(repr(r.stdout))

try:
    subprocess.run(["false"], check=True)
except subprocess.CalledProcessError as e:
    print("raised:", e)

r = subprocess.run("echo hi | tr a-z A-Z", shell=True, capture_output=True, text=True)
print(r.stdout.strip())
Text
2 '' ls: cannot access '/nonexistent': No such file or directory
'héllo\n'
raised: Command '['false']' returned non-zero exit status 1.
HI

subprocess.run waits for the command and returns a CompletedProcess. With capture_output=True the output and error streams are collected in stdout and stderr, and text=True decodes them to strings, so the accented word came back as a string with its trailing newline. A failed ls has return code 2 with its message in stderr. Adding check=True turns a non-zero exit into CalledProcessError, as the false example shows. Both keywords need Python 3.7 or later. See subprocess.run.

Pass the command as a list, which avoids the shell and its quoting. shell=True is needed for a pipe, as in the last call, but with input from outside it opens the door to command injection. The output is held in memory, so use Popen for very large or endless output.

Share

More snippets

Get new posts by email

One email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.