Bibliotecas Esenciales: os, sys, shutil y subprocess
Python es un lenguaje extraordinariamente versátil cuando se trata de automatizar tareas del sistema operativo. Cuatro bibliotecas fundamentales forman el núcleo de cualquier script de automatización profesional: os, sys, shutil y subprocess. En esta lección aprenderás a dominar estas herramientas para crear scripts potentes que interactúen con tu sistema operativo de manera eficiente y segura.
¿Por qué estas cuatro bibliotecas?
Cada biblioteca cumple un propósito específico y complementario. Mientras os y sys proporcionan funcionalidades del sistema y del intérprete, shutil maneja operaciones de alto nivel con archivos, y subprocess permite ejecutar comandos del sistema externo. Juntas, forman un kit completo para automatización.
La Biblioteca os: Tu Ventana al Sistema de Archivos
La biblioteca os es probablemente la más utilizada para interactuar con el sistema operativo. Nos permite navegar por el árbol de directorios, crear carpetas, obtener información del sistema y mucho más.
Operaciones con Directorios
import os
# Obtener el directorio de trabajo actual
directorio_actual = os.getcwd()
print(f"Directorio actual: {directorio_actual}")
# Listar archivos en un directorio
archivos = os.listdir('./proyecto')
for archivo in archivos:
print(f" - {archivo}")
# Crear un directorio
os.makedirs('./nueva_carpeta', exist_ok=True)
# Unir rutas de manera segura
ruta_completa = os.path.join('C:', 'Users', 'Documentos', 'archivo.txt')
print(f"Ruta construida: {ruta_completa}")
# Verificar existencia
if os.path.exists('./configuracion.json'):
print("El archivo de configuración existe")
# Obtener información del archivo
info = os.stat('./configuracion.json')
print(f"Tamaño: {info.st_size} bytes")
print(f"Última modificación: {info.st_mtime}")
Nota importante: Siempre usa
os.path.join()en lugar de concatenar strings con el operador+. Esto garantiza que tu código funcione correctamente en Windows, Linux y macOS.
La Biblioteca sys: Control del Intérprete Python
La biblioteca sys nos proporciona acceso a variables y funciones específicas del intérprete de Python. Es esencial para manejar argumentos de línea de comandos y controlar el flujo del programa.
import sys
# Manejo de argumentos de línea de comandos
print(f"Nombre del script: {sys.argv[0]}")
print(f"Argumentos: {sys.argv[1:]}")
# Verificar versión de Python
print(f"Versión de Python: {sys.version}")
print(f"Versión mayor: {sys.version_info.major}")
# Rutas de búsqueda de módulos
print(f"PYTHONPATH: {sys.path}")
# Salir del programa con código de error
def validar_entrada(datos):
if not datos:
print("Error: No se proporcionaron datos", file=sys.stderr)
sys.exit(1) # Código de salida 1 indica error
return True
# Ejemplo práctico: script que procesa archivos
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Uso: python script.py <archivo_entrada> <archivo_salida>")
sys.exit(1)
archivo_entrada = sys.argv[1]
archivo_salida = sys.argv[2] if len(sys.argv) > 2 else "salida.txt"
print(f"Procesando {archivo_entrada} -> {archivo_salida}")
La Biblioteca shutil: Operaciones de Alto Nivel con Archivos
Mientras os trabaja con funciones de bajo nivel, shutil ofrece operaciones de alto nivel más complejas como copiar, mover y eliminar árboles de directorios completos.
import shutil
import os
# Copiar un archivo
shutil.copy('documento.txt', 'backup/documento.txt')
# Copiar un archivo con metadatos
shutil.copy2('imagen.png', 'backup/imagen.png')
# Copiar un directorio completo
shutil.copytree('./datos', './backup/datos')
# Mover archivos o directorios
shutil.move('./temp/archivo.txt', './procesado/archivo.txt')
# Mover un directorio completo
shutil.move('./proyecto_viejo', './proyecto_nuevo')
# Eliminar un directorio y todo su contenido
shutil.rmtree('./temporal', ignore_errors=True)
# Obtener el tamaño total de un directorio
def tamano_directorio(ruta):
total = 0
for dirpath, dirnames, filenames in os.walk(ruta):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
total += os.path.getsize(filepath)
return total
tamano_mb = tamano_directorio('./datos') / (1024 * 1024)
print(f"Tamaño total: {tamano_mb:.2f} MB")
# Crear un archivo ZIP de un directorio
shutil.make_archive('backup_proyecto', 'zip', './proyecto')
Precaución:
shutil.rmtree()elimina permanentemente los archivos. Úsalo con precaución y considera implementar una papelera de reciclaje personalizada para operaciones críticas.
La Biblioteca subprocess: Ejecutando Comandos del Sistema
subprocess es la herramienta definitiva para ejecutar comandos externos, aplicaciones y scripts de otros lenguajes desde Python. Es especialmente útil para integrar herramientas existentes en tus flujos de trabajo.
import subprocess
# Ejecutar un comando simple
resultado = subprocess.run(['ls', '-la'], capture_output=True, text=True)
print(resultado.stdout)
print(resultado.stderr)
# Ejecutar con shell=True (menos seguro, más flexible)
resultado = subprocess.run('dir', shell=True, capture_output=True, text=True)
# Ejecutar y obtener el código de salida
proceso = subprocess.run(['python', 'script.py'], capture_output=True)
if proceso.returncode == 0:
print("Script ejecutado exitosamente")
else:
print(f"Error: código de salida {proceso.returncode}")
# Ejemplo práctico: automatizar Git
comandos_git = [
['git', 'add', '.'],
['git', 'commit', '-m', 'Actualización automática'],
['git', 'push', 'origin', 'main']
]
for cmd in comandos_git:
resultado = subprocess.run(cmd, capture_output=True, text=True)
if resultado.returncode != 0:
print(f"Error en comando: {' '.join(cmd)}")
print(resultado.stderr)
break
print(f"✓ Ejecutado: {' '.join(cmd)}")
# Ejemplo: Comprimir imágenes con ImageMagick
def comprimir_imagen(ruta_imagen):
comando = [
'convert',
ruta_imagen,
'-quality', '80',
'-resize', '1200x1200>',
ruta_imagen
]
resultado = subprocess.run(comando, capture_output=True)
return resultado.returncode == 0
# Ejecutar múltiples comandos en paralelo
import concurrent.futures
def ejecutar_backup(archivo):
subprocess.run(['cp', archivo, f'./backup/{archivo}'])
archivos = ['datos1.csv', 'datos2.csv', 'datos3.csv']
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
executor.map(ejecutar_backup, archivos)
Script de Automatización Integrado: Respaldo Automatizado
Ahora combinaremos las cuatro bibliotecas en un script de respaldo automatizado completo:
#!/usr/bin/env python3
"""
Script de respaldo automatizado
Uso: python backup_script.py <directorio_origen> <directorio_destino>
"""
import os
import sys
import shutil
import subprocess
from datetime import datetime
def crear_backup(origen, destino):
# Validar que el origen existe
if not os.path.exists(origen):
print(f"Error: El directorio {origen} no existe", file=sys.stderr)
sys.exit(1)
# Crear nombre del backup con timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
nombre_backup = f"backup_{os.path.basename(origen)}_{timestamp}"
ruta_backup = os.path.join(destino, nombre_backup)
# Crear directorio de destino si no existe
os.makedirs(destino, exist_ok=True)
# Copiar directorio
print(f"Creando backup de {origen}...")
try:
shutil.copytree(origen, ruta_backup)
print(f"✓ Backup creado en: {ruta_backup}")
except Exception as e:
print(f"Error al crear backup: {e}", file=sys.stderr)
sys.exit(1)
# Comprimir el backup
print("Comprimiendo backup...")
shutil.make_archive(ruta_backup, 'zip', ruta_backup)
shutil.rmtree(ruta_backup) # Eliminar carpeta no comprimida
print(f"✓ Backup comprimido: {ruta_backup}.zip")
# Mostrar tamaño del archivo
tamano = os.path.getsize(f"{ruta_backup}.zip")
print(f" Tamaño: {tamano / (1024*1024):.2f} MB")
return f"{ruta_backup}.zip"
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Uso: python backup_script.py <origen> <destino>")
sys.exit(1)
origen = sys.argv[1]
destino = sys.argv[2]
crear_backup(origen, destino)
Errores Comunes
- Usar rutas absolutas mezcladas con relativas: Uno de los errores más frecuentes es asumir que el directorio de trabajo actual es el esperado. Siempre verifica con
os.getcwd()y usaos.path.abspath()para normalizar rutas. Además, ten cuidado con~que el shell expande pero Python no entiende sinos.path.expanduser(). - No manejar excepciones en subprocess: Ejecutar comandos externos sin capturar posibles errores genera scripts frágiles. Siempre verifica
returncode, capturastderry maneja timeouts consubprocess.run(..., timeout=30)para evitar procesos colgados. - Ignorar diferencias entre sistemas operativos: Comandos como
dir(Windows) vsls(Linux/Mac) romperán tu script. Usaos.namepara detectar el sistema o, mejor aún, usa bibliotecas multiplataforma. Verifica también las barras invertidas vs normales en rutas Windows.
Checklist de Dominio
- ✓ Puedo navegar por el sistema de archivos usando
os.path.join(),os.listdir()yos.walk() - ✓ Manejo correctamente argumentos de línea de comandos con
sys.argv - ✓ Utilizo
shutilpara copiar, mover y comprimir directorios completos - ✓ Ejecuto comandos externos con
subprocess.run()y verifico códigos de retorno - ✓ Implemento manejo de excepciones para todas las operaciones del sistema
- ✓ Construyo rutas de manera portable con
os.path - ✓ Creo scripts reutilizables que aceptan parámetros por línea de comandos
- ✓ Combino las cuatro bibliotecas en soluciones de automatización completas
- ✓ Verifico existencia de archivos y directorios antes de operar
- ✓ Implemento logging para auditar operaciones automatizadas