Module 07 - Sous-processus & Commandes Système
Exécuter des commandes système et interagir avec le shell.
Durée estimée : 15 minutes
Objectifs du Module
- Exécuter des commandes avec subprocess
- Capturer et traiter les sorties
- Gérer les erreurs et timeouts
- Utiliser os et shutil pour les opérations système
1. subprocess.run() - Méthode Moderne
Exécution Simple
import subprocess
# Commande simple
result = subprocess.run(["ls", "-la"])
print(f"Return code: {result.returncode}")
# Avec capture de sortie
result = subprocess.run(
["ls", "-la", "/var/log"],
capture_output=True,
text=True # Decode en string (sinon bytes)
)
print(result.stdout)
print(result.stderr)
print(result.returncode)
Options Importantes
import subprocess
# capture_output=True équivaut à :
result = subprocess.run(
["command"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Fusionner stderr dans stdout
result = subprocess.run(
["command"],
capture_output=True,
text=True,
stderr=subprocess.STDOUT
)
# Timeout (en secondes)
try:
result = subprocess.run(
["sleep", "10"],
timeout=5
)
except subprocess.TimeoutExpired:
print("Command timed out!")
# Vérifier le code retour (exception si != 0)
try:
result = subprocess.run(
["grep", "pattern", "/nonexistent"],
check=True,
capture_output=True,
text=True
)
except subprocess.CalledProcessError as e:
print(f"Command failed with code {e.returncode}")
print(f"stderr: {e.stderr}")
Exécution avec Shell
# Sans shell (plus sûr, recommandé)
result = subprocess.run(["ls", "-la"], capture_output=True, text=True)
# Avec shell (nécessaire pour pipes, wildcards, etc.)
result = subprocess.run(
"ls -la | grep .py | wc -l",
shell=True,
capture_output=True,
text=True
)
# ⚠️ DANGER : Injection de commandes avec shell=True
user_input = "file.txt; rm -rf /" # Malicieux!
# NE JAMAIS FAIRE :
subprocess.run(f"cat {user_input}", shell=True)
# Sécurisé : utiliser shlex.quote ou liste d'arguments
import shlex
subprocess.run(f"cat {shlex.quote(user_input)}", shell=True)
# Ou mieux : sans shell
subprocess.run(["cat", user_input])
Répertoire de Travail et Environnement
import subprocess
import os
# Changer le répertoire de travail
result = subprocess.run(
["ls"],
cwd="/var/log",
capture_output=True,
text=True
)
# Variables d'environnement personnalisées
my_env = os.environ.copy()
my_env["MY_VAR"] = "value"
my_env["PATH"] = f"/custom/bin:{my_env['PATH']}"
result = subprocess.run(
["my_script.sh"],
env=my_env,
capture_output=True,
text=True
)
# Ou ajouter uniquement quelques variables
result = subprocess.run(
["printenv", "MY_VAR"],
env={**os.environ, "MY_VAR": "value"},
capture_output=True,
text=True
)
2. Entrée Standard (stdin)
import subprocess
# Passer des données en entrée
result = subprocess.run(
["grep", "error"],
input="line1\nerror line\nline3\n",
capture_output=True,
text=True
)
print(result.stdout) # "error line\n"
# Depuis une variable
log_content = open("/var/log/messages").read()
result = subprocess.run(
["grep", "-c", "ERROR"],
input=log_content,
capture_output=True,
text=True
)
# Depuis un fichier
with open("/var/log/messages") as f:
result = subprocess.run(
["grep", "ERROR"],
stdin=f,
capture_output=True,
text=True
)
3. Popen - Contrôle Avancé
Exécution Asynchrone
import subprocess
import time
# Lancer un processus en arrière-plan
process = subprocess.Popen(
["long_running_command"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# Vérifier si terminé
while process.poll() is None:
print("Still running...")
time.sleep(1)
# Récupérer la sortie
stdout, stderr = process.communicate()
print(f"Exit code: {process.returncode}")
Lecture en Temps Réel
import subprocess
# Lire la sortie ligne par ligne
process = subprocess.Popen(
["tail", "-f", "/var/log/messages"],
stdout=subprocess.PIPE,
text=True
)
try:
for line in process.stdout:
print(f"LOG: {line.strip()}")
if "CRITICAL" in line:
break
finally:
process.terminate()
process.wait()
Pipes entre Processus
import subprocess
# ps aux | grep python | wc -l
ps = subprocess.Popen(
["ps", "aux"],
stdout=subprocess.PIPE
)
grep = subprocess.Popen(
["grep", "python"],
stdin=ps.stdout,
stdout=subprocess.PIPE
)
wc = subprocess.Popen(
["wc", "-l"],
stdin=grep.stdout,
stdout=subprocess.PIPE,
text=True
)
ps.stdout.close()
grep.stdout.close()
output = wc.communicate()[0]
print(f"Python processes: {output.strip()}")
4. Module os - Opérations Système
Variables d'Environnement
import os
# Lire
home = os.getenv("HOME")
user = os.environ.get("USER", "unknown")
path = os.environ["PATH"]
# Modifier (pour le processus courant)
os.environ["MY_APP_DEBUG"] = "true"
# Supprimer
del os.environ["MY_APP_DEBUG"]
# Lister toutes les variables
for key, value in os.environ.items():
print(f"{key}={value}")
Informations Système
import os
# Répertoires
os.getcwd() # Répertoire courant
os.chdir("/tmp") # Changer de répertoire
# Utilisateur
os.getuid() # User ID
os.getgid() # Group ID
os.getlogin() # Nom de login
# Système
os.uname() # Info système (Linux)
os.cpu_count() # Nombre de CPUs
os.getpid() # PID du processus
os.getppid() # PID du parent
Opérations sur Fichiers/Répertoires
import os
# Test d'existence
os.path.exists("/var/log")
os.path.isfile("/etc/passwd")
os.path.isdir("/var/log")
os.path.islink("/usr/bin/python")
# Création
os.mkdir("/tmp/mydir")
os.makedirs("/tmp/a/b/c", exist_ok=True)
# Suppression
os.remove("/tmp/file.txt") # Fichier
os.rmdir("/tmp/empty_dir") # Dossier vide
# Renommage
os.rename("/tmp/old.txt", "/tmp/new.txt")
# Listing
os.listdir("/var/log") # Liste des fichiers
# Parcours récursif
for root, dirs, files in os.walk("/var/log"):
for file in files:
filepath = os.path.join(root, file)
print(filepath)
5. Module shutil - Opérations de Haut Niveau
import shutil
# Copie de fichiers
shutil.copy("/src/file.txt", "/dst/file.txt") # Copie fichier
shutil.copy2("/src/file.txt", "/dst/") # Préserve métadonnées
# Copie de répertoires
shutil.copytree("/src/dir", "/dst/dir")
shutil.copytree("/src", "/dst", dirs_exist_ok=True) # Python 3.8+
# Déplacement
shutil.move("/src/file.txt", "/dst/file.txt")
# Suppression récursive
shutil.rmtree("/tmp/mydir")
shutil.rmtree("/tmp/mydir", ignore_errors=True)
# Archive
shutil.make_archive("/tmp/backup", "zip", "/data")
shutil.make_archive("/tmp/backup", "gztar", "/data")
shutil.unpack_archive("/tmp/backup.zip", "/restore")
# Espace disque
total, used, free = shutil.disk_usage("/")
print(f"Total: {total // (1024**3)} GB")
print(f"Free: {free // (1024**3)} GB")
# Trouver un exécutable
shutil.which("python3") # /usr/bin/python3
6. Cas d'Usage SysOps
Wrapper de Commandes
import subprocess
from dataclasses import dataclass
from typing import Optional
@dataclass
class CommandResult:
command: str
returncode: int
stdout: str
stderr: str
success: bool
def run_command(
cmd: list,
timeout: int = 60,
cwd: Optional[str] = None
) -> CommandResult:
"""Exécute une commande et retourne un résultat structuré."""
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=timeout,
cwd=cwd
)
return CommandResult(
command=" ".join(cmd),
returncode=result.returncode,
stdout=result.stdout,
stderr=result.stderr,
success=result.returncode == 0
)
except subprocess.TimeoutExpired:
return CommandResult(
command=" ".join(cmd),
returncode=-1,
stdout="",
stderr="Command timed out",
success=False
)
# Usage
result = run_command(["systemctl", "status", "nginx"])
if result.success:
print("Nginx is running")
else:
print(f"Error: {result.stderr}")
Service Manager
import subprocess
class ServiceManager:
"""Gestionnaire de services systemd."""
def __init__(self, service_name: str):
self.service = service_name
def _run_systemctl(self, action: str) -> tuple:
result = subprocess.run(
["systemctl", action, self.service],
capture_output=True,
text=True
)
return result.returncode == 0, result.stderr
def start(self) -> bool:
success, _ = self._run_systemctl("start")
return success
def stop(self) -> bool:
success, _ = self._run_systemctl("stop")
return success
def restart(self) -> bool:
success, _ = self._run_systemctl("restart")
return success
def status(self) -> dict:
result = subprocess.run(
["systemctl", "is-active", self.service],
capture_output=True,
text=True
)
is_active = result.stdout.strip() == "active"
result = subprocess.run(
["systemctl", "is-enabled", self.service],
capture_output=True,
text=True
)
is_enabled = result.stdout.strip() == "enabled"
return {
"name": self.service,
"active": is_active,
"enabled": is_enabled
}
# Usage
nginx = ServiceManager("nginx")
print(nginx.status())
nginx.restart()
Exercices Pratiques
Exercice 1 : Health Checker
# Créer un script qui :
# - Vérifie si des services sont actifs (systemctl)
# - Vérifie l'espace disque (df)
# - Vérifie la charge système (uptime)
# - Retourne un rapport JSON
Exercice 2 : Backup Script
# Créer un script de backup qui :
# - Utilise tar pour créer une archive
# - Gère les erreurs et timeouts
# - Affiche la progression
Points Clés à Retenir
Bonnes Pratiques
- Toujours utiliser
subprocess.run()plutôt queos.system() - Éviter
shell=Truequand possible - Toujours capturer stderr
- Utiliser des timeouts
- Vérifier les codes de retour
Sécurité
Voir Aussi
Navigation
| ← Module 06 - Formats de Données | Module 08 - Expressions Régulières → |