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 |