Drill 05: Subprocess & System
subprocess.run, environment variables, and system command patterns.
Run This Drill
bash ~/atelier/_bibliotheca/domus-captures/docs/modules/ROOT/examples/python-drills/05-subprocess.sh
Drill Script
#!/bin/bash
# PYTHON DRILL 05: SUBPROCESS & SYSTEM
# Paste this entire script into your terminal
# Topics: subprocess, os, environment, command execution
echo "=================================================================="
echo " PYTHON DRILL 05: SUBPROCESS & SYSTEM "
echo "=================================================================="
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 5.1: SUBPROCESS.RUN (BASIC)"
echo "Execute shell commands"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import subprocess
# Simple command
result = subprocess.run(["echo", "Hello from subprocess"])
print(f"Return code: {result.returncode}")
# Capture output
result = subprocess.run(
["hostname"],
capture_output=True,
text=True
)
print(f"\nHostname: {result.stdout.strip()}")
# Command with arguments
result = subprocess.run(
["ls", "-la", "/tmp"],
capture_output=True,
text=True
)
print(f"\nFirst 3 lines of ls -la /tmp:")
for line in result.stdout.split("\n")[:3]:
print(f" {line}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 5.2: SHELL=TRUE VS LIST"
echo "When to use shell mode"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import subprocess
# List form (preferred - safer)
result = subprocess.run(
["grep", "-c", "root", "/etc/passwd"],
capture_output=True,
text=True
)
print(f"grep result: {result.stdout.strip()}")
# Shell form (needed for pipes, redirects)
result = subprocess.run(
"cat /etc/passwd | head -3",
shell=True,
capture_output=True,
text=True
)
print(f"\nWith shell pipe:")
print(result.stdout)
# Shell for complex commands
result = subprocess.run(
"ls /tmp/*.txt 2>/dev/null | wc -l",
shell=True,
capture_output=True,
text=True
)
print(f".txt files in /tmp: {result.stdout.strip()}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 5.3: ERROR HANDLING"
echo "Check return codes and stderr"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import subprocess
# Check for errors
result = subprocess.run(
["ls", "/nonexistent"],
capture_output=True,
text=True
)
print(f"Return code: {result.returncode}")
print(f"Stderr: {result.stderr.strip()}")
# Raise exception on failure
try:
subprocess.run(
["ls", "/nonexistent"],
capture_output=True,
text=True,
check=True # Raises CalledProcessError on non-zero
)
except subprocess.CalledProcessError as e:
print(f"\nCaught error: {e}")
print(f"Stderr: {e.stderr}")
# Safe pattern
def run_cmd(cmd):
result = subprocess.run(
cmd,
capture_output=True,
text=True
)
if result.returncode != 0:
return None, result.stderr
return result.stdout.strip(), None
output, error = run_cmd(["whoami"])
print(f"\nwhoami: {output}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 5.4: ENVIRONMENT VARIABLES"
echo "Read and set env vars"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import os
# Read env var
user = os.environ.get("USER", "unknown")
home = os.environ.get("HOME", "/tmp")
print(f"USER: {user}")
print(f"HOME: {home}")
# Get with default
api_key = os.environ.get("API_KEY", "not-set")
print(f"API_KEY: {api_key}")
# Set env var for subprocess
import subprocess
# Pass modified environment
my_env = os.environ.copy()
my_env["MY_VAR"] = "hello"
result = subprocess.run(
["bash", "-c", "echo $MY_VAR"],
capture_output=True,
text=True,
env=my_env
)
print(f"\nMY_VAR in subprocess: {result.stdout.strip()}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 5.5: PRACTICAL PATTERNS"
echo "Real infrastructure scripts"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import subprocess
import json
# Get JSON output from command
def run_json(cmd):
result = subprocess.run(
cmd,
capture_output=True,
text=True,
check=True
)
return json.loads(result.stdout)
# Simulated - would be: kubectl get pods -o json
# For demo, use echo
result = subprocess.run(
["echo", '{"items": [{"name": "pod1"}, {"name": "pod2"}]}'],
capture_output=True,
text=True
)
data = json.loads(result.stdout)
print(f"Pods: {[p['name'] for p in data['items']]}")
# Run multiple commands
commands = [
["hostname"],
["date", "+%Y-%m-%d"],
["whoami"]
]
print("\n=== System info ===")
for cmd in commands:
result = subprocess.run(cmd, capture_output=True, text=True)
print(f" {cmd[0]}: {result.stdout.strip()}")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "DRILL 5.6: TIMEOUT AND ASYNC"
echo "Handle long-running commands"
echo "------------------------------------------------------------------"
echo ""
python3 << 'PYEOF'
import subprocess
# Timeout
try:
result = subprocess.run(
["sleep", "10"],
timeout=2,
capture_output=True
)
except subprocess.TimeoutExpired:
print("Command timed out after 2 seconds")
# Background process (don't wait)
proc = subprocess.Popen(
["sleep", "1"],
stdout=subprocess.PIPE
)
print(f"Started process: PID {proc.pid}")
print(f"Still running: {proc.poll() is None}")
# Wait with timeout
try:
proc.wait(timeout=2)
print(f"Process finished: {proc.returncode}")
except subprocess.TimeoutExpired:
proc.kill()
print("Killed long-running process")
PYEOF
echo ""
# ---------------------------------------------------------------------------
echo "------------------------------------------------------------------"
echo "YOUR TURN - TRY THESE:"
echo "------------------------------------------------------------------"
echo ""
echo "1. Get disk usage:"
echo " import subprocess"
echo " result = subprocess.run(['df', '-h', '/'], capture_output=True, text=True)"
echo " print(result.stdout)"
echo ""
echo "2. Check if host is reachable:"
echo " result = subprocess.run(['ping', '-c', '1', '-W', '1', '8.8.8.8'],"
echo " capture_output=True)"
echo " print('Reachable' if result.returncode == 0 else 'Unreachable')"
echo ""
echo "3. Get env var with fallback:"
echo " import os"
echo " vault_addr = os.environ.get('VAULT_ADDR', 'https://127.0.0.1:8200')"
echo ""
echo "------------------------------------------------------------------"
echo "KEY TAKEAWAYS:"
echo "1. subprocess.run([cmd, args], capture_output=True, text=True)"
echo "2. Use list form for safety, shell=True for pipes"
echo "3. check=True raises exception on failure"
echo "4. os.environ.get('VAR', 'default') for env vars"
echo "5. timeout= prevents hangs"
echo "------------------------------------------------------------------"