Capturing Command Output and Error Streams

🏷️ Operating System and System Operations / Shell Commands with Subprocess

When running shell commands from Python, the output and error messages produced by those commands are sent to separate streams. The standard output (stdout) carries normal results, while the standard error (stderr) carries error messages, warnings, and diagnostic information. Capturing both streams properly is essential for building reliable automation scripts that can handle success and failure scenarios gracefully.


⚙️ Understanding Output Streams

Every command run in a shell produces two distinct streams of data:

  • Standard Output (stdout) – Contains the normal result or data produced by the command. This is what you typically see when a command runs successfully.
  • Standard Error (stderr) – Contains error messages, warnings, and debugging information. This stream is separate from stdout, even when a command succeeds.

Capturing both streams allows you to: - Process successful output separately from error messages. - Log errors for troubleshooting without mixing them with normal results. - Make decisions in your script based on whether errors occurred.


🛠️ Capturing Output with subprocess.run

The subprocess.run function provides several options for capturing output streams:

  • Set capture_output=True to capture both stdout and stderr into memory.
  • Access the captured output through the .stdout and .stderr attributes of the returned object.
  • Use text=True to get the output as a string instead of bytes.

A typical pattern looks like this: - Import the subprocess module. - Call subprocess.run with your command as a list of arguments. - Set capture_output=True and text=True. - Access result.stdout for normal output and result.stderr for error output.


📊 Comparison: Capturing vs. Not Capturing

Behavior Without Capture With Capture
Output visibility Printed directly to terminal Stored in memory, not shown
Error handling Errors appear on screen Errors stored separately
Processing results Cannot use output in code Can analyze, log, or transform output
Memory usage Minimal Uses memory for stored output

🕵️ Handling Errors and Non-Zero Exit Codes

Commands that fail typically return a non-zero exit code. Python's subprocess module can detect this:

  • Check the .returncode attribute after running a command.
  • A return code of 0 means success; any other value indicates an error.
  • Use the check=True parameter to automatically raise an exception when a command fails.
  • When using check=True, wrap the call in a try-except block to handle the subprocess.CalledProcessError exception.

The exception object contains: - .returncode – The exit code from the failed command. - .output – The captured stdout (if capture_output was enabled). - .stderr – The captured stderr (if capture_output was enabled).


🔄 Redirecting stderr to stdout

Sometimes you want to combine both streams into a single output for simpler processing:

  • Use the stderr parameter with the value subprocess.STDOUT.
  • This redirects error messages into the stdout stream.
  • After redirection, only result.stdout contains all output, and result.stderr will be empty.

This is useful when: - You want to log everything in one place. - Error messages contain useful diagnostic information you want to process. - You are building a unified output for reporting purposes.


📝 Practical Patterns for Engineers

When building automation scripts, consider these common patterns:

  • Success-only capture – Use capture_output=True and check returncode before processing stdout. If the command failed, handle the error separately.
  • Always capture both – Use capture_output=True with text=True and always examine both .stdout and .stderr for complete information.
  • Fail-fast with exceptions – Use check=True when a command must succeed for the script to continue. Catch the exception to log details and exit gracefully.
  • Silent execution with error logging – Capture both streams but only log stderr when the return code is non-zero. Suppress normal output unless debugging.

⚠️ Common Pitfalls to Avoid

  • Forgetting text=True – Without this, output is returned as bytes, requiring manual decoding with .decode().
  • Ignoring stderr – Even successful commands may write warnings to stderr that contain important information.
  • Not checking return codes – A command may produce output on stdout but still fail. Always verify the return code.
  • Capturing large outputs – For commands that produce massive output, consider streaming to a file instead of storing in memory.
  • Mixing stdout and stderr unintentionally – Be deliberate about whether you want them combined or separate.

🔍 When to Use Each Approach

  • Use separate streams when you need to distinguish between normal results and errors for different processing paths.
  • Use combined streams when building a single log entry or when error messages are part of the expected output.
  • Use check=True in critical automation steps where failure should stop the entire process.
  • Use manual return code checking when you want to handle failures gracefully and continue with alternative logic.

By mastering output and error stream capture, you gain full control over how command results flow through your Python scripts, enabling robust error handling and clean data processing.


This topic shows how to run shell commands from Python and capture their standard output and standard error streams separately.

📘 Example 1: Capturing standard output from a simple command

This example runs echo and captures its printed text.

import subprocess

result = subprocess.run(
    ["echo", "Hello, engineer"],
    capture_output=True,
    text=True
)
output_text = result.stdout

📤 Output: "Hello, engineer\n"


📘 Example 2: Capturing standard error from a failing command

This example runs a command that produces an error and captures the error stream.

import subprocess

result = subprocess.run(
    ["ls", "/nonexistent_directory"],
    capture_output=True,
    text=True
)
error_text = result.stderr

📤 Output: "ls: cannot access '/nonexistent_directory': No such file or directory\n"


📘 Example 3: Checking return code and capturing both streams

This example shows how to check if a command succeeded and capture both output and error.

import subprocess

result = subprocess.run(
    ["cat", "/etc/hostname"],
    capture_output=True,
    text=True
)
if result.returncode == 0:
    print("Success:", result.stdout)
else:
    print("Failed:", result.stderr)

📤 Output: "Success: my-hostname\n" (or similar hostname)


📘 Example 4: Running a pipeline and capturing output

This example runs a shell pipeline using shell=True and captures the final output.

import subprocess

result = subprocess.run(
    "ps aux | grep python | head -3",
    capture_output=True,
    text=True,
    shell=True
)
output_lines = result.stdout.strip().split("\n")

📤 Output: List of first 3 Python processes (varies by system)


📘 Example 5: Capturing output and error separately with timeout

This example runs a command with a timeout and captures both streams safely.

import subprocess

try:
    result = subprocess.run(
        ["ping", "-c", "4", "google.com"],
        capture_output=True,
        text=True,
        timeout=10
    )
    print("STDOUT:", result.stdout[:100])
    print("STDERR:", result.stderr)
except subprocess.TimeoutExpired:
    print("Command timed out")

📤 Output: "STDOUT: PING google.com (142.250.80.14) 56(84) bytes of data.\n64 bytes from ..." (truncated)


Comparison Table

Feature result.stdout result.stderr result.returncode
What it holds Standard output text Standard error text Exit code (0 = success)
Data type String (if text=True) String (if text=True) Integer
Empty when Command produces no output Command runs without errors Always present
Useful for Reading command results Debugging failures Checking success/failure

When running shell commands from Python, the output and error messages produced by those commands are sent to separate streams. The standard output (stdout) carries normal results, while the standard error (stderr) carries error messages, warnings, and diagnostic information. Capturing both streams properly is essential for building reliable automation scripts that can handle success and failure scenarios gracefully.


⚙️ Understanding Output Streams

Every command run in a shell produces two distinct streams of data:

  • Standard Output (stdout) – Contains the normal result or data produced by the command. This is what you typically see when a command runs successfully.
  • Standard Error (stderr) – Contains error messages, warnings, and debugging information. This stream is separate from stdout, even when a command succeeds.

Capturing both streams allows you to: - Process successful output separately from error messages. - Log errors for troubleshooting without mixing them with normal results. - Make decisions in your script based on whether errors occurred.


🛠️ Capturing Output with subprocess.run

The subprocess.run function provides several options for capturing output streams:

  • Set capture_output=True to capture both stdout and stderr into memory.
  • Access the captured output through the .stdout and .stderr attributes of the returned object.
  • Use text=True to get the output as a string instead of bytes.

A typical pattern looks like this: - Import the subprocess module. - Call subprocess.run with your command as a list of arguments. - Set capture_output=True and text=True. - Access result.stdout for normal output and result.stderr for error output.


📊 Comparison: Capturing vs. Not Capturing

Behavior Without Capture With Capture
Output visibility Printed directly to terminal Stored in memory, not shown
Error handling Errors appear on screen Errors stored separately
Processing results Cannot use output in code Can analyze, log, or transform output
Memory usage Minimal Uses memory for stored output

🕵️ Handling Errors and Non-Zero Exit Codes

Commands that fail typically return a non-zero exit code. Python's subprocess module can detect this:

  • Check the .returncode attribute after running a command.
  • A return code of 0 means success; any other value indicates an error.
  • Use the check=True parameter to automatically raise an exception when a command fails.
  • When using check=True, wrap the call in a try-except block to handle the subprocess.CalledProcessError exception.

The exception object contains: - .returncode – The exit code from the failed command. - .output – The captured stdout (if capture_output was enabled). - .stderr – The captured stderr (if capture_output was enabled).


🔄 Redirecting stderr to stdout

Sometimes you want to combine both streams into a single output for simpler processing:

  • Use the stderr parameter with the value subprocess.STDOUT.
  • This redirects error messages into the stdout stream.
  • After redirection, only result.stdout contains all output, and result.stderr will be empty.

This is useful when: - You want to log everything in one place. - Error messages contain useful diagnostic information you want to process. - You are building a unified output for reporting purposes.


📝 Practical Patterns for Engineers

When building automation scripts, consider these common patterns:

  • Success-only capture – Use capture_output=True and check returncode before processing stdout. If the command failed, handle the error separately.
  • Always capture both – Use capture_output=True with text=True and always examine both .stdout and .stderr for complete information.
  • Fail-fast with exceptions – Use check=True when a command must succeed for the script to continue. Catch the exception to log details and exit gracefully.
  • Silent execution with error logging – Capture both streams but only log stderr when the return code is non-zero. Suppress normal output unless debugging.

⚠️ Common Pitfalls to Avoid

  • Forgetting text=True – Without this, output is returned as bytes, requiring manual decoding with .decode().
  • Ignoring stderr – Even successful commands may write warnings to stderr that contain important information.
  • Not checking return codes – A command may produce output on stdout but still fail. Always verify the return code.
  • Capturing large outputs – For commands that produce massive output, consider streaming to a file instead of storing in memory.
  • Mixing stdout and stderr unintentionally – Be deliberate about whether you want them combined or separate.

🔍 When to Use Each Approach

  • Use separate streams when you need to distinguish between normal results and errors for different processing paths.
  • Use combined streams when building a single log entry or when error messages are part of the expected output.
  • Use check=True in critical automation steps where failure should stop the entire process.
  • Use manual return code checking when you want to handle failures gracefully and continue with alternative logic.

By mastering output and error stream capture, you gain full control over how command results flow through your Python scripts, enabling robust error handling and clean data processing.

Interactive Views

You are currently in 📚 All-in-One mode. Use the tabs at the top to switch to 📖 Theory Only or 💻 Code Only views.

This topic shows how to run shell commands from Python and capture their standard output and standard error streams separately.

📘 Example 1: Capturing standard output from a simple command

This example runs echo and captures its printed text.

import subprocess

result = subprocess.run(
    ["echo", "Hello, engineer"],
    capture_output=True,
    text=True
)
output_text = result.stdout

📤 Output: "Hello, engineer\n"


📘 Example 2: Capturing standard error from a failing command

This example runs a command that produces an error and captures the error stream.

import subprocess

result = subprocess.run(
    ["ls", "/nonexistent_directory"],
    capture_output=True,
    text=True
)
error_text = result.stderr

📤 Output: "ls: cannot access '/nonexistent_directory': No such file or directory\n"


📘 Example 3: Checking return code and capturing both streams

This example shows how to check if a command succeeded and capture both output and error.

import subprocess

result = subprocess.run(
    ["cat", "/etc/hostname"],
    capture_output=True,
    text=True
)
if result.returncode == 0:
    print("Success:", result.stdout)
else:
    print("Failed:", result.stderr)

📤 Output: "Success: my-hostname\n" (or similar hostname)


📘 Example 4: Running a pipeline and capturing output

This example runs a shell pipeline using shell=True and captures the final output.

import subprocess

result = subprocess.run(
    "ps aux | grep python | head -3",
    capture_output=True,
    text=True,
    shell=True
)
output_lines = result.stdout.strip().split("\n")

📤 Output: List of first 3 Python processes (varies by system)


📘 Example 5: Capturing output and error separately with timeout

This example runs a command with a timeout and captures both streams safely.

import subprocess

try:
    result = subprocess.run(
        ["ping", "-c", "4", "google.com"],
        capture_output=True,
        text=True,
        timeout=10
    )
    print("STDOUT:", result.stdout[:100])
    print("STDERR:", result.stderr)
except subprocess.TimeoutExpired:
    print("Command timed out")

📤 Output: "STDOUT: PING google.com (142.250.80.14) 56(84) bytes of data.\n64 bytes from ..." (truncated)


Comparison Table

Feature result.stdout result.stderr result.returncode
What it holds Standard output text Standard error text Exit code (0 = success)
Data type String (if text=True) String (if text=True) Integer
Empty when Command produces no output Command runs without errors Always present
Useful for Reading command results Debugging failures Checking success/failure