Recommended Execution with subprocess.run()

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

🧠 Context Introduction

When working with Python, you often need to run shell commandsβ€”whether it's checking disk space, pinging a server, or managing files. The subprocess module is the modern, secure way to execute system commands from within Python. Among its many functions, subprocess.run() is the recommended approach for most tasks because it is simple, reliable, and handles errors gracefully.

This guide will help you understand why subprocess.run() is the preferred method and how to use it effectively in your daily automation tasks.


  • Simplicity – It replaces older, more complex functions like os.system() and subprocess.call() with a clean, unified interface.
  • Safety – It avoids shell injection vulnerabilities by default (when used without shell=True).
  • Flexibility – It returns a CompletedProcess object that gives you access to the exit code, stdout, and stderr.
  • Error Handling – You can easily check if a command succeeded or failed using the return code.

πŸ› οΈ Basic Usage Pattern

The core idea is simple: you pass a list of command arguments to subprocess.run() and it executes the command for you.

  • The command is provided as a list of strings, where the first element is the executable and the rest are its arguments.
  • The function returns a CompletedProcess object that contains:
  • returncode – Exit code (0 means success, non-zero means failure)
  • stdout – Standard output (captured if you set capture_output=True)
  • stderr – Standard error (captured if you set capture_output=True)

Example of a simple command execution: - subprocess.run(["ls", "-l"]) – Lists files in the current directory. - subprocess.run(["ping", "-c", "4", "google.com"]) – Pings google.com four times.


πŸ“Š Comparison: subprocess.run() vs Older Methods

Feature subprocess.run() os.system() subprocess.call()
Return Type CompletedProcess object Exit code (integer) Exit code (integer)
Capture Output Yes, with capture_output=True No No
Shell Injection Risk Low (default) High Low
Error Handling Easy with returncode Manual check needed Manual check needed
Modern Standard βœ… Yes ❌ Legacy ❌ Legacy

πŸ•΅οΈ Capturing Output and Errors

One of the biggest advantages of subprocess.run() is the ability to capture what a command prints to the screen.

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

Example flow: - Run a command like subprocess.run(["df", "-h"], capture_output=True, text=True) - Check the result.stdout to see the disk space information. - Check result.stderr if something went wrong.


βœ… Checking Command Success

After running a command, you almost always want to know if it succeeded or failed.

  • Use result.returncode to check the exit code.
  • A return code of 0 means success.
  • Any non-zero value indicates an error.
  • You can also use result.check_returncode() to raise an exception if the command failed.

Example logic: - If result.returncode == 0, proceed with your automation. - If result.returncode != 0, log the error from result.stderr and handle it appropriately.


🌐 Running Commands with Shell Features

Sometimes you need shell features like pipes, wildcards, or environment variable expansion.

  • Set shell=True to run the command through the system shell.
  • Important: Only use shell=True with trusted input to avoid security risks.
  • When using shell=True, pass the command as a single string instead of a list.

Example: - subprocess.run("echo $HOME", shell=True, capture_output=True, text=True) – Prints the home directory path.


⏱️ Setting Timeouts

Commands can sometimes hang indefinitely. Protect your automation with timeouts.

  • Use the timeout parameter to specify a maximum execution time in seconds.
  • If the command exceeds the timeout, a TimeoutExpired exception is raised.
  • This is especially useful for network commands like ping or curl.

Example: - subprocess.run(["sleep", "10"], timeout=5) – This would raise a timeout error after 5 seconds.


πŸ“ Practical Tips for Daily Use

  • Always use capture_output=True when you need to inspect command output.
  • Use text=True to work with strings instead of bytes.
  • Prefer passing commands as lists over strings to avoid shell injection.
  • Use check=True if you want the command to raise an exception automatically on failure.
  • Store frequently used commands in variables or functions to avoid repetition.

πŸ§ͺ Common Workflow Pattern

A typical pattern for running a command and handling its result looks like this:

  • Define your command as a list: ["command", "arg1", "arg2"]
  • Call subprocess.run() with your command and desired options.
  • Check the returncode to determine success or failure.
  • Access stdout or stderr for further processing or logging.
  • Handle any exceptions like FileNotFoundError or TimeoutExpired.

This pattern keeps your code clean, predictable, and easy to debug.


πŸ“Œ Summary

  • subprocess.run() is the modern, recommended way to execute shell commands in Python.
  • It returns a CompletedProcess object with exit code, stdout, and stderr.
  • Use capture_output=True and text=True for easy output handling.
  • Always check the returncode to confirm command success.
  • Use timeout to prevent hanging commands.
  • Prefer list-based commands over strings for security.
  • Reserve shell=True for cases where you truly need shell features.

By mastering subprocess.run(), you gain a powerful and safe tool for automating system tasks directly from your Python scripts.


subprocess.run() is Python's recommended way to execute shell commands and capture their output from within a Python script.

πŸ› οΈ Example 1: Running a simple command

This shows how to run a basic shell command and wait for it to finish.

import subprocess

result = subprocess.run(["echo", "Hello from Python"])

πŸ“€ Output: Hello from Python


πŸ› οΈ Example 2: Capturing command output

This demonstrates how to capture the output of a command as a string.

import subprocess

result = subprocess.run(["whoami"], capture_output=True, text=True)
print(result.stdout)

πŸ“€ Output: your_username


πŸ› οΈ Example 3: Checking if a command succeeded

This shows how to verify that a command completed without errors.

import subprocess

result = subprocess.run(["ls", "/nonexistent"], capture_output=True, text=True)
if result.returncode == 0:
    print("Command succeeded")
else:
    print("Command failed with return code:", result.returncode)
    print("Error message:", result.stderr)

πŸ“€ Output: Command failed with return code: 2
Error message: ls: cannot access '/nonexistent': No such file or directory


πŸ› οΈ Example 4: Running a command with arguments and environment variables

This shows how to pass arguments and set environment variables for a command.

import subprocess
import os

env = os.environ.copy()
env["MY_VAR"] = "engineers"

result = subprocess.run(
    ["printenv", "MY_VAR"],
    capture_output=True,
    text=True,
    env=env
)
print(result.stdout.strip())

πŸ“€ Output: engineers


πŸ› οΈ Example 5: Running a pipeline of commands

This demonstrates how to chain multiple commands together using shell=True.

import subprocess

result = subprocess.run(
    "ps aux | grep python | head -3",
    capture_output=True,
    text=True,
    shell=True
)
print(result.stdout)

πŸ“€ Output: user 12345 0.0 0.1 12345 6789 pts/0 S+ 12:34 0:00 python script.py
(shows up to 3 lines of running Python processes)


πŸ“Š Comparison Table

Feature subprocess.run() os.system() os.popen()
Captures output βœ… Yes ❌ No βœ… Yes
Returns exit code βœ… Yes βœ… Yes ❌ No
Safe from shell injection βœ… Yes (default) ❌ No ❌ No
Recommended by Python docs βœ… Yes ❌ No ❌ No
Works on all OS βœ… Yes βœ… Yes βœ… Yes

🧠 Context Introduction

When working with Python, you often need to run shell commandsβ€”whether it's checking disk space, pinging a server, or managing files. The subprocess module is the modern, secure way to execute system commands from within Python. Among its many functions, subprocess.run() is the recommended approach for most tasks because it is simple, reliable, and handles errors gracefully.

This guide will help you understand why subprocess.run() is the preferred method and how to use it effectively in your daily automation tasks.


  • Simplicity – It replaces older, more complex functions like os.system() and subprocess.call() with a clean, unified interface.
  • Safety – It avoids shell injection vulnerabilities by default (when used without shell=True).
  • Flexibility – It returns a CompletedProcess object that gives you access to the exit code, stdout, and stderr.
  • Error Handling – You can easily check if a command succeeded or failed using the return code.

πŸ› οΈ Basic Usage Pattern

The core idea is simple: you pass a list of command arguments to subprocess.run() and it executes the command for you.

  • The command is provided as a list of strings, where the first element is the executable and the rest are its arguments.
  • The function returns a CompletedProcess object that contains:
  • returncode – Exit code (0 means success, non-zero means failure)
  • stdout – Standard output (captured if you set capture_output=True)
  • stderr – Standard error (captured if you set capture_output=True)

Example of a simple command execution: - subprocess.run(["ls", "-l"]) – Lists files in the current directory. - subprocess.run(["ping", "-c", "4", "google.com"]) – Pings google.com four times.


πŸ“Š Comparison: subprocess.run() vs Older Methods

Feature subprocess.run() os.system() subprocess.call()
Return Type CompletedProcess object Exit code (integer) Exit code (integer)
Capture Output Yes, with capture_output=True No No
Shell Injection Risk Low (default) High Low
Error Handling Easy with returncode Manual check needed Manual check needed
Modern Standard βœ… Yes ❌ Legacy ❌ Legacy

πŸ•΅οΈ Capturing Output and Errors

One of the biggest advantages of subprocess.run() is the ability to capture what a command prints to the screen.

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

Example flow: - Run a command like subprocess.run(["df", "-h"], capture_output=True, text=True) - Check the result.stdout to see the disk space information. - Check result.stderr if something went wrong.


βœ… Checking Command Success

After running a command, you almost always want to know if it succeeded or failed.

  • Use result.returncode to check the exit code.
  • A return code of 0 means success.
  • Any non-zero value indicates an error.
  • You can also use result.check_returncode() to raise an exception if the command failed.

Example logic: - If result.returncode == 0, proceed with your automation. - If result.returncode != 0, log the error from result.stderr and handle it appropriately.


🌐 Running Commands with Shell Features

Sometimes you need shell features like pipes, wildcards, or environment variable expansion.

  • Set shell=True to run the command through the system shell.
  • Important: Only use shell=True with trusted input to avoid security risks.
  • When using shell=True, pass the command as a single string instead of a list.

Example: - subprocess.run("echo $HOME", shell=True, capture_output=True, text=True) – Prints the home directory path.


⏱️ Setting Timeouts

Commands can sometimes hang indefinitely. Protect your automation with timeouts.

  • Use the timeout parameter to specify a maximum execution time in seconds.
  • If the command exceeds the timeout, a TimeoutExpired exception is raised.
  • This is especially useful for network commands like ping or curl.

Example: - subprocess.run(["sleep", "10"], timeout=5) – This would raise a timeout error after 5 seconds.


πŸ“ Practical Tips for Daily Use

  • Always use capture_output=True when you need to inspect command output.
  • Use text=True to work with strings instead of bytes.
  • Prefer passing commands as lists over strings to avoid shell injection.
  • Use check=True if you want the command to raise an exception automatically on failure.
  • Store frequently used commands in variables or functions to avoid repetition.

πŸ§ͺ Common Workflow Pattern

A typical pattern for running a command and handling its result looks like this:

  • Define your command as a list: ["command", "arg1", "arg2"]
  • Call subprocess.run() with your command and desired options.
  • Check the returncode to determine success or failure.
  • Access stdout or stderr for further processing or logging.
  • Handle any exceptions like FileNotFoundError or TimeoutExpired.

This pattern keeps your code clean, predictable, and easy to debug.


πŸ“Œ Summary

  • subprocess.run() is the modern, recommended way to execute shell commands in Python.
  • It returns a CompletedProcess object with exit code, stdout, and stderr.
  • Use capture_output=True and text=True for easy output handling.
  • Always check the returncode to confirm command success.
  • Use timeout to prevent hanging commands.
  • Prefer list-based commands over strings for security.
  • Reserve shell=True for cases where you truly need shell features.

By mastering subprocess.run(), you gain a powerful and safe tool for automating system tasks directly from your Python scripts.

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.

subprocess.run() is Python's recommended way to execute shell commands and capture their output from within a Python script.

πŸ› οΈ Example 1: Running a simple command

This shows how to run a basic shell command and wait for it to finish.

import subprocess

result = subprocess.run(["echo", "Hello from Python"])

πŸ“€ Output: Hello from Python


πŸ› οΈ Example 2: Capturing command output

This demonstrates how to capture the output of a command as a string.

import subprocess

result = subprocess.run(["whoami"], capture_output=True, text=True)
print(result.stdout)

πŸ“€ Output: your_username


πŸ› οΈ Example 3: Checking if a command succeeded

This shows how to verify that a command completed without errors.

import subprocess

result = subprocess.run(["ls", "/nonexistent"], capture_output=True, text=True)
if result.returncode == 0:
    print("Command succeeded")
else:
    print("Command failed with return code:", result.returncode)
    print("Error message:", result.stderr)

πŸ“€ Output: Command failed with return code: 2
Error message: ls: cannot access '/nonexistent': No such file or directory


πŸ› οΈ Example 4: Running a command with arguments and environment variables

This shows how to pass arguments and set environment variables for a command.

import subprocess
import os

env = os.environ.copy()
env["MY_VAR"] = "engineers"

result = subprocess.run(
    ["printenv", "MY_VAR"],
    capture_output=True,
    text=True,
    env=env
)
print(result.stdout.strip())

πŸ“€ Output: engineers


πŸ› οΈ Example 5: Running a pipeline of commands

This demonstrates how to chain multiple commands together using shell=True.

import subprocess

result = subprocess.run(
    "ps aux | grep python | head -3",
    capture_output=True,
    text=True,
    shell=True
)
print(result.stdout)

πŸ“€ Output: user 12345 0.0 0.1 12345 6789 pts/0 S+ 12:34 0:00 python script.py
(shows up to 3 lines of running Python processes)


πŸ“Š Comparison Table

Feature subprocess.run() os.system() os.popen()
Captures output βœ… Yes ❌ No βœ… Yes
Returns exit code βœ… Yes βœ… Yes ❌ No
Safe from shell injection βœ… Yes (default) ❌ No ❌ No
Recommended by Python docs βœ… Yes ❌ No ❌ No
Works on all OS βœ… Yes βœ… Yes βœ… Yes