Running External Commands with subprocess.run/shaare/K5w00Q
Running External Commands with subprocess.run
- DevOps automation often requires invoking existing CLI tools or scripts to leverage their functionality without re-implementing it in Python.
- The
subprocessmodule provides a secure and flexible interface to spawn child processes, control their input/output streams, and inspect their exit statuses. - The modern recommended method is
subprocess.run(), which combines execution, output capture, and error handling in a single call.
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('Hello from subprocess.')"],
capture_output=True,
text=True
)
print(f"Return code: {result.returncode}")
print(f"Stdout: {result.stdout.strip()}")
Why subprocess? The Old Ways
- Older approaches like
os.system()invoke a shell directly, making them vulnerable to injection and offering limited control over I/O streams. - The
subprocessmodule was introduced to provide finer control, better security, and a consistent API across platforms. - Functions such as
subprocess.call(),check_output(), andPopenexist, butsubprocess.run()(Python 3.5+) simplifies most common use cases into one interface.
+++
The subprocess.run() Function
argsshould be a list of strings where the first element is the command and the rest are its parameters.capture_output=Truecaptures bothstdoutandstderrinto the returnedCompletedProcess.text=Truedecodes bytes into strings using the system’s default encoding.check=Trueraises aCalledProcessErrorfor non-zero exit codes, allowing you to handle failures via exceptions.shell=False(the default) avoids invoking a shell, preventing injection vulnerabilities; useshell=Trueonly if you fully control the command string.- The returned
CompletedProcesshas attributesargs,returncode,stdout, andstderrfor introspection.
import subprocess
import sys
cmd = [
sys.executable,
"-c",
"""print("Hello from subprocess")
invalid_function()"""
]
result = subprocess.run(cmd, capture_output=True, text=True)
print(f"Args: {result.args}")
print(f"Stdout: {result.stdout.strip()}")
print(f"Stderr: {result.stderr.strip()}")
print(f"Return code: {result.returncode}")
Basic Command Execution
- Construct your command as a list, choosing the tool and its arguments explicitly.
- Use
capture_output=Trueandtext=Trueto get human-readable strings. - Inspect
result.returncodeto determine if the command succeeded (zero) or failed (non-zero).
import subprocess
import platform
if platform.system() == "Windows":
cmd = ["ver"]
else:
cmd = ["uname", "-a"]
result = subprocess.run(cmd, capture_output=True, text=True)
print(result.stdout.strip())
Common Pitfalls & How to Avoid Them
- Forgetting
capture_output=Truemeansresult.stdoutandresult.stderrwill beNone, so you cannot inspect them. - Omitting
text=Trueleaves you with raw bytes that require manual decoding. - Using
check=Falsewithout checkingresult.returncodecan let failures go unnoticed. - Invoking a shell with
shell=Trueand untrusted input enables injection attacks—always prefershell=False.
pythonpython
(97)