How do I execute a program or call a system command?
Categories:
Executing External Programs and System Commands in Python

Learn how to reliably execute shell commands and external programs from your Python scripts using the subprocess module, covering basic calls, capturing output, and handling errors.
Python offers robust ways to interact with the underlying operating system, including executing external programs and system commands. This capability is crucial for tasks like automating system administration, integrating with command-line tools, or running scripts written in other languages. While older methods like os.system and os.popen exist, the subprocess module is the recommended and most powerful approach for modern Python development.
The subprocess Module: Your Go-To for External Commands
The subprocess module allows you to spawn new processes, connect to their input/output/error pipes, and obtain their return codes. It replaces older, less flexible functions and provides a unified interface for process management. The primary function you'll use is subprocess.run().
flowchart TD
A[Python Script] --> B{"subprocess.run()"}
B --> C[External Command/Program]
C --> D[Standard Output (stdout)]
C --> E[Standard Error (stderr)]
C --> F[Return Code]
D --> B
E --> B
F --> B
B --> G[Python Script (Result)]Flow of executing an external command using subprocess.run()
Basic Command Execution
The simplest way to execute a command is by passing it as a list of arguments to subprocess.run(). This is generally safer than passing a single string with shell=True because it avoids shell injection vulnerabilities. For commands without arguments, a single string can be used, but the list format is preferred for consistency.
import subprocess
# Example 1: Simple command without arguments
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print("Stdout:", result.stdout)
print("Stderr:", result.stderr)
print("Return Code:", result.returncode)
# Example 2: Command with arguments
result = subprocess.run(['echo', 'Hello, World!'], capture_output=True, text=True)
print("Stdout:", result.stdout)
# Example 3: Running a command that might fail
result = subprocess.run(['non_existent_command'], capture_output=True, text=True)
print("Return Code for failed command:", result.returncode)
print("Stderr for failed command:", result.stderr)
Basic execution of shell commands using subprocess.run()
['ls', '-l']) rather than a single string (e.g., 'ls -l') unless you specifically need shell features and understand the security implications of shell=True.Capturing Output and Handling Errors
To capture the standard output (stdout) and standard error (stderr) of the executed command, you need to set capture_output=True. By default, subprocess.run() will print stdout and stderr directly to your console. Setting text=True (or encoding='utf-8') ensures that the captured output is decoded as text rather than bytes.
import subprocess
# Command that produces output
result = subprocess.run(
['grep', 'root', '/etc/passwd'],
capture_output=True,
text=True,
check=False # Do not raise an exception for non-zero exit codes
)
print("Captured Stdout:\n", result.stdout)
print("Captured Stderr:\n", result.stderr)
print("Exit Code:", result.returncode)
# Command that fails and we want to check for it
try:
subprocess.run(
['false_command'],
capture_output=True,
text=True,
check=True # Raise CalledProcessError for non-zero exit codes
)
except subprocess.CalledProcessError as e:
print(f"Command failed with error: {e}")
print(f"Stderr: {e.stderr}")
Capturing output and handling non-zero exit codes with check=True
check=True argument is crucial for robust error handling. If the executed command returns a non-zero exit code (indicating an error), subprocess.run() will raise a CalledProcessError exception, which you can then catch and handle gracefully.Advanced Scenarios: Input, Shell, and Timeouts
The subprocess module offers more advanced features for complex interactions:
- Input (
inputparameter): You can pass data to the standard input (stdin) of the child process. - Shell (
shell=True): If you need shell features like wildcards, environment variables, or command chaining (e.g.,command1 | command2), you can setshell=True. However, be extremely cautious as this can introduce security vulnerabilities if the command string comes from untrusted input. - Timeout (
timeoutparameter): Prevent commands from running indefinitely by setting a timeout. If the command exceeds the timeout, aTimeoutExpiredexception is raised.
import subprocess
import time
# Example 1: Providing input to a command
# This simulates 'cat' reading from stdin
result = subprocess.run(
['cat'],
input='This is input for cat.',
capture_output=True,
text=True
)
print("Cat output with input:", result.stdout)
# Example 2: Using shell=True (use with caution!)
# This command uses shell redirection
result = subprocess.run(
'echo "Hello from shell" > output.txt',
shell=True,
capture_output=True,
text=True
)
print("Shell command executed. Check output.txt")
# Example 3: Command with a timeout
try:
# 'sleep 5' will run for 5 seconds
subprocess.run(['sleep', '5'], timeout=2, capture_output=True, text=True)
except subprocess.TimeoutExpired:
print("Command timed out after 2 seconds!")
# Clean up the created file
subprocess.run(['rm', 'output.txt'], capture_output=True)
Advanced subprocess.run() usage with input, shell, and timeout
subprocess.Popen directly, which gives you more fine-grained control over the process lifecycle.