← Back to Chapters

Python subprocess Module

? Python subprocess Module

⚡ Quick Overview

The subprocess module lets your Python program start new processes, connect to their input/output/error pipes, and obtain their return codes. It is the recommended way to run external commands and replace older modules like os.system() and os.popen().

Common use cases include automating command-line tools, running shell scripts, capturing command output, and integrating system utilities into Python applications.

Import it like this: import subprocess

? Key Concepts

  • subprocess.run() – High-level API to run a command and wait for it to finish.
  • Popen class – Low-level interface for advanced control over processes.
  • CompletedProcess – Object returned by run() containing output and status.
  • Standard streamsstdin, stdout, and stderr pipes for communicating with the process.
  • check=True – Raises an exception if the command exits with a non-zero status.
  • capture_output=True / stdout & stderr – Capture the command’s output.
  • text=True / encoding=... – Get output as strings instead of bytes.
  • shell=True – Runs the command through the shell (use with care for security).

? Syntax and Core APIs

? Using subprocess.run()

Basic pattern:

? View Basic subprocess.run() Syntax
import subprocess

# General pattern
result = subprocess.run(
    ["command", "arg1", "arg2"],  # list of program + arguments
    capture_output=False,         # True to capture stdout/stderr
    text=False,                   # True to get str instead of bytes
    check=False,                  # True to raise error on non-zero exit
    shell=False                   # True to execute through the shell
)

print(result.returncode)

The run() function returns a CompletedProcess object with attributes like:

  • args – The command that was run.
  • returncode – Exit status (0 means success on most systems).
  • stdout – Captured standard output (if requested).
  • stderr – Captured standard error (if requested).

? Using Popen for Advanced Control

Popen gives you full control: you can interact with the process while it is running, send data, and read incremental output.

? View Popen Syntax
import subprocess

# Start a subprocess and set up pipes for communication
proc = subprocess.Popen(
    ["command", "arg1"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    text=True
)

# Send optional input and read all output
stdout_data, stderr_data = proc.communicate(input="optional input\n")
print("Return code:", proc.returncode)

? Code Examples

? Run a Simple Command

Example: run the echo command and print its output.

? View Code Example
import subprocess

# Run a simple echo command and capture its output
result = subprocess.run(
    ["echo", "Hello from subprocess!"],
    capture_output=True,
    text=True
)

# Inspect the return code and captured streams
print("Return code:", result.returncode)
print("STDOUT:", result.stdout.strip())
print("STDERR:", result.stderr)

? Capture Output and Handle Errors

Use check=True to raise an exception if the command fails.

? View Code Example
import subprocess

# Run a command and raise an error if it fails
try:
    result = subprocess.run(
        ["python", "--version"],
        capture_output=True,
        text=True,
        check=True
    )
    print("Command succeeded!")
    print("Output:", result.stdout or result.stderr)
# Handle the case where the command exits with a non-zero status
except subprocess.CalledProcessError as e:
    print("Command failed with return code:", e.returncode)
    print("Output:", e.stdout)
    print("Error:", e.stderr)

? Piping Input to a Command

Send data to a command via stdin using input.

? View Code Example
import subprocess

text = "line 1\nline 2\nline 3\n"

# Run wc -l and pass the text through stdin
result = subprocess.run(
    ["wc", "-l"],            # counts lines on Unix-like systems
    input=text,
    capture_output=True,
    text=True
)

print("Input text:")
print(text)
print("\nNumber of lines reported by wc -l:", result.stdout.strip())

⚠️ Using shell=True Carefully

shell=True lets you use shell features like wildcards and pipes, but can be dangerous if you use untrusted input (command injection risk).

? View Code Example
import subprocess

# Example that uses shell features (pipe + grep)
cmd = "ps aux | grep python"

result = subprocess.run(
    cmd,
    shell=True,
    capture_output=True,
    text=True
)

print("Running:", cmd)
print(result.stdout)

?️ Live Output and Explanation

? What happens in the simple echo example?

When we run:

# Run the echo command and capture its output
result = subprocess.run(
    ["echo", "Hello from subprocess!"],
    capture_output=True,
    text=True
)
  • The operating system starts a new process to run the echo program.
  • capture_output=True tells Python to capture both stdout and stderr.
  • text=True decodes the output into a normal Python string.
  • After the process finishes, result.returncode contains the exit status (0 for success).
  • result.stdout contains "Hello from subprocess!\n".

In a real terminal you would see something like:

Return code: 0
STDOUT: Hello from subprocess!
STDERR: None

? Tips and Best Practices

  • Prefer subprocess.run() for most use cases; use Popen only when you need advanced control.
  • Use a list (["cmd", "arg1", "arg2"]) instead of a string to avoid shell parsing issues.
  • Avoid shell=True with untrusted input; if you must use it, sanitize or hard-code the command string.
  • Use check=True in scripts where failures should immediately stop execution.
  • Use capture_output=True or stdout=subprocess.PIPE only if you really need the output—large outputs can use a lot of memory.
  • For long-running commands, consider Popen and communicate() or iterate over stdout to stream output.
  • Always handle exceptions like FileNotFoundError (command not found) and CalledProcessError.

? Common Use Cases

  • Automating system maintenance scripts (backups, cleanups, deployments).
  • Calling existing CLI tools (e.g., git, ffmpeg, curl) from Python.
  • Integrating shell scripts into larger Python applications.
  • Collecting diagnostics by running tools like ping, traceroute, or top.
  • Building wrappers around legacy command-line programs.

? Try It Yourself – Practice Tasks

  1. Write a Python script that uses subprocess.run() to:
    • Run dir (Windows) or ls (Linux/macOS).
    • Capture and print the output.
    • Print the return code.
  2. Create a script that:
    • Asks the user for a URL.
    • Uses subprocess.run() to call ping for that URL (e.g., ping -c 4 example.com).
    • Displays whether the ping command succeeded or failed based on returncode.
  3. Experiment with Popen:
    • Start a long-running command (e.g., ping with many packets).
    • Read a few lines from stdout.
    • Terminate the process programmatically.
  4. Compare os.system() vs subprocess.run() in a small example. Note the advantages of subprocess.