Python subprocess 模块

run vs Popen, capturing output, timeouts, and why shell=True is dangerous.

Intermediate
Key PointsPass a list, never a shell string; always set a timeout and check returncode.

subprocess.run

run executes and waits, returning a CompletedProcess with returncode, stdout and stderr.

Python
import subprocess

r = subprocess.run(["ls", "-l", "/tmp"], capture_output=True, text=True, timeout=10)
print(r.returncode)
print(r.stdout.splitlines()[0])
Output
0
total 48

Errors and Timeouts

A failing command does not raise — check returncode or pass check=True. Timeouts raise TimeoutExpired and kill the child.

Python
import subprocess

try:
    subprocess.run(["false"], check=True, timeout=5)
except subprocess.CalledProcessError as exc:
    print("failed with", exc.returncode)

try:
    subprocess.run(["sleep", "5"], timeout=1)
except subprocess.TimeoutExpired:
    print("timed out")
Output
failed with 1
timed out

Shell Injection

shell=True hands the string to a shell, so ;, | and $() in user input get executed. A list argument never touches the shell.

Python
import subprocess

name = "a; rm -rf /"          # 用户输入

# 危险:整个字符串进 shell
# subprocess.run(f"echo {name}", shell=True)

# 安全:列表参数,不经过 shell
r = subprocess.run(["echo", name], capture_output=True, text=True)
print(r.stdout.strip())
Output
a; rm -rf /

Streaming Output

Python
import subprocess

with subprocess.Popen(["ping", "-c", "2", "127.0.0.1"],
                      stdout=subprocess.PIPE, text=True) as proc:
    for line in proc.stdout:
        print(line.rstrip())
    print("exit", proc.wait())
If you truly need shell features, keep the command fixed and pass variables via env=, never by concatenation.

Friends & Partners