Python - Python subprocess Module for External Process Management
The Python subprocess module is used to create and manage external processes from a Python program. An external process is any program that runs separately from the current Python program, such as a system command, another Python script, a shell utility, or an installed application. The subprocess module allows Python to start these programs, provide input to them, capture their output, check their exit status, and handle errors. It is particularly useful when a Python application needs to interact with operating-system commands or other programs.
1. Importing the subprocess Module
The module can be imported using:
import subprocess
After importing it, Python provides several functions and classes for process management. The most commonly used function is subprocess.run(), which is designed for running a command and waiting for it to finish.
For example:
import subprocess
result = subprocess.run(["python", "--version"])
print("Command completed")
Here, Python starts another process to execute the command and waits until that process finishes.
2. Using subprocess.run()
The subprocess.run() function is the recommended approach for most simple process-management tasks.
A basic example is:
import subprocess
result = subprocess.run(["echo", "Hello from Python"])
print(result.returncode)
The command is passed as a list. The first element is the program to execute, while the remaining elements are its arguments.
The returncode attribute contains the exit status of the process. Usually, a value of 0 indicates successful execution, while a non-zero value generally indicates that an error occurred.
For example:
import subprocess
result = subprocess.run(["python", "--version"])
if result.returncode == 0:
print("Command executed successfully")
else:
print("Command failed")
3. Capturing Standard Output
By default, the output of a subprocess may be displayed directly in the terminal. Python can instead capture that output for further processing.
The capture_output=True argument captures both standard output and standard error.
import subprocess
result = subprocess.run(
["python", "--version"],
capture_output=True,
text=True
)
print(result.stdout)
print(result.stderr)
The stdout attribute contains the standard output, while stderr contains error output.
The text=True argument tells Python to return the output as a string instead of bytes.
4. Passing Input to a Process
A Python program can also send input to an external process.
For example:
import subprocess
result = subprocess.run(
["python", "-c", "name = input(); print('Hello', name)"],
input="Alice\n",
capture_output=True,
text=True
)
print(result.stdout)
The input parameter supplies data to the standard input of the subprocess.
This is useful when one program needs to communicate with another program automatically rather than requiring a user to enter information manually.
5. Handling Errors with check=True
Normally, subprocess.run() returns a result even when the command fails. The check=True argument changes this behavior.
import subprocess
subprocess.run(["python", "missing_file.py"], check=True)
If the external process finishes with a non-zero exit status, Python raises a subprocess.CalledProcessError.
This can be handled using try and except:
import subprocess
try:
subprocess.run(
["python", "missing_file.py"],
check=True
)
except subprocess.CalledProcessError:
print("The command failed")
This approach is useful when failure should immediately be treated as an exception.
6. Setting a Timeout
A subprocess might take longer than expected to finish. Python allows a maximum execution time to be specified using the timeout parameter.
import subprocess
try:
result = subprocess.run(
["python", "long_running_program.py"],
timeout=10
)
except subprocess.TimeoutExpired:
print("The process took too long")
In this example, Python waits for a maximum of 10 seconds. If the process does not finish within that period, a TimeoutExpired exception is raised.
Timeouts are especially useful for applications that need to prevent an external process from running indefinitely.
7. Using Popen for More Control
subprocess.Popen provides lower-level control over a running process. Unlike subprocess.run(), which normally waits for the command to finish, Popen can start a process and allow the Python program to continue working while that process runs.
import subprocess
process = subprocess.Popen(["python", "--version"])
print("Process started")
process.wait()
print("Process finished")
The Popen object represents the running process.
The wait() method waits for the process to finish and returns its exit status.
Popen is useful when an application needs more detailed interaction with a process, particularly when communicating with its standard input, output, or error streams.
8. Communicating with a Running Process
The communicate() method can be used to send input to a process and receive its output.
import subprocess
process = subprocess.Popen(
["python", "-c", "name = input(); print('Hello', name)"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
)
output, error = process.communicate("Alice\n")
print(output)
This provides a convenient way to communicate with a subprocess while also waiting for it to complete.
The standard streams can be connected using:
stdin=subprocess.PIPE
stdout=subprocess.PIPE
stderr=subprocess.PIPE
This allows the Python program to control the communication channels of the external process.
9. Understanding Standard Input, Output, and Error
External processes generally have three standard streams.
Standard input (stdin) is used to provide input to a process.
Standard output (stdout) contains normal output produced by the process.
Standard error (stderr) contains error messages and diagnostic information.
Python can connect to these streams using the subprocess module.
For example:
import subprocess
result = subprocess.run(
["python", "--version"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print("Output:", result.stdout)
print("Errors:", result.stderr)
Understanding these streams is important when building applications that need to capture or process information generated by external programs.
10. Running Commands Through the Shell
The shell parameter determines whether a command is executed through the system shell.
For example:
import subprocess
result = subprocess.run(
"echo Hello",
shell=True,
capture_output=True,
text=True
)
print(result.stdout)
Although shell=True can be convenient for shell-specific commands, it should be used carefully.
If a command contains input originating from an untrusted user, using shell=True can create command-injection vulnerabilities. For safer execution, commands should generally be passed as a list without invoking the shell:
subprocess.run(["echo", "Hello"])
This is an important security consideration when using the subprocess module.
11. Practical Example
Suppose a Python application needs to check whether a particular program is available on the system.
import subprocess
try:
result = subprocess.run(
["python", "--version"],
capture_output=True,
text=True,
check=True
)
print("Python version:")
print(result.stdout or result.stderr)
except FileNotFoundError:
print("Python was not found")
except subprocess.CalledProcessError:
print("The command could not be executed")
Here, several features are combined. Python starts an external process, captures its output, checks the exit status, and handles possible errors.
12. Advantages of the subprocess Module
The subprocess module provides several important advantages:
-
It allows Python programs to execute external applications.
-
It can capture output generated by other programs.
-
It allows input to be passed to external processes.
-
It provides access to the process exit status.
-
It supports timeouts for long-running commands.
-
It provides detailed process control through
Popen. -
It allows programs to communicate through standard input and output streams.
-
It provides mechanisms for handling process-related errors.
13. Difference Between run() and Popen()
The two commonly used approaches serve different purposes.
| Feature | subprocess.run() |
subprocess.Popen() |
|---|---|---|
| Ease of use | Simple | More advanced |
| Waits for completion | Yes, by default | No |
| Process control | Limited | Extensive |
| Capture output | Supported | Supported |
| Interactive communication | Limited | Stronger |
| Suitable for | Simple commands | Complex process management |
In general, subprocess.run() should be preferred when a program simply needs to execute a command and wait for the result. Popen is more appropriate when the application needs detailed control over a process while it is running.
Conclusion
The Python subprocess module provides a powerful way to communicate with programs and commands outside the current Python process. It can execute commands, capture their output, provide input, check exit codes, handle failures, enforce timeouts, and manage long-running processes. subprocess.run() is suitable for straightforward tasks, while subprocess.Popen() provides greater control for advanced process management. Because external commands can introduce security risks, particularly when shell execution and user-provided input are involved, commands should be constructed carefully and untrusted input should never be passed directly to a shell.