Python
Tutorial Python: Módulo os
Domina el módulo os en Python para gestionar archivos, rutas, procesos y variables de entorno de forma multiplataforma y eficiente.
Aprende Python y certifícateInteracción con el sistema operativo
El módulo os
de Python actúa como un puente entre tu código y el sistema operativo subyacente, permitiéndote realizar operaciones que normalmente requerirían comandos específicos de shell. Esta capacidad es fundamental para crear aplicaciones que necesiten interactuar con el entorno del sistema.
La interacción con el sistema operativo mediante el módulo os
te permite realizar operaciones como obtener información del sistema, ejecutar comandos y gestionar procesos de manera independiente de la plataforma. Esto significa que puedes escribir código que funcione tanto en Windows como en sistemas Unix/Linux con mínimas modificaciones.
Importación del módulo
Para comenzar a utilizar el módulo os
, primero debes importarlo:
import os
Información del sistema
Una de las funcionalidades básicas es obtener información sobre el sistema operativo en el que se está ejecutando tu programa:
# Identificar el sistema operativo
print(os.name) # Devuelve 'posix' (Unix/Linux/Mac) o 'nt' (Windows)
# Obtener información más detallada del sistema
if hasattr(os, 'uname'): # Solo disponible en sistemas tipo Unix
print(os.uname())
El atributo os.name
proporciona una identificación básica del tipo de sistema operativo, mientras que os.uname()
ofrece información más detallada en sistemas Unix.
Acceso a variables del entorno
El módulo os
permite acceder y modificar las variables de entorno del sistema:
# Obtener el valor de una variable de entorno
home_dir = os.environ.get('HOME') # En Unix/Linux/Mac
# O en Windows:
user_profile = os.environ.get('USERPROFILE')
# Establecer una nueva variable de entorno
os.environ['MI_VARIABLE'] = 'valor'
# Listar todas las variables de entorno
for key, value in os.environ.items():
print(f"{key}: {value}")
El diccionario os.environ
te permite acceder a todas las variables de entorno del sistema, lo que resulta útil para configurar aplicaciones basadas en el entorno de ejecución.
Operaciones con directorios
El módulo os
proporciona funciones para trabajar con directorios:
# Obtener el directorio de trabajo actual
directorio_actual = os.getcwd()
print(f"Estás en: {directorio_actual}")
# Cambiar el directorio de trabajo
os.chdir('/ruta/a/otro/directorio')
# Crear un nuevo directorio
os.mkdir('nuevo_directorio')
# Crear directorios anidados (equivalente a mkdir -p)
os.makedirs('directorio/subdirectorio/otro', exist_ok=True)
# Eliminar un directorio
os.rmdir('directorio_vacio') # Solo funciona si está vacío
# Eliminar directorios recursivamente
import shutil # Módulo complementario para operaciones de archivos
shutil.rmtree('directorio_con_contenido') # ¡Cuidado! Elimina todo el contenido
La función os.getcwd()
es especialmente útil para determinar la ubicación desde la que se está ejecutando tu script, mientras que os.chdir()
te permite navegar por el sistema de archivos.
Listado de archivos y directorios
Para explorar el contenido de un directorio:
# Listar archivos y directorios
contenido = os.listdir('.') # '.' representa el directorio actual
print(f"Contenido del directorio: {contenido}")
# Filtrar solo archivos
archivos = [f for f in contenido if os.path.isfile(f)]
print(f"Archivos: {archivos}")
# Filtrar solo directorios
directorios = [d for d in contenido if os.path.isdir(d)]
print(f"Directorios: {directorios}")
# Listar con información detallada
for elemento in contenido:
tipo = "Directorio" if os.path.isdir(elemento) else "Archivo"
tamaño = os.path.getsize(elemento) # Tamaño en bytes
print(f"{elemento} - {tipo} - {tamaño} bytes")
La combinación de os.listdir()
con las funciones de os.path
te permite analizar y filtrar el contenido de directorios de manera eficiente.
Ejecución de comandos del sistema
El módulo os
permite ejecutar comandos del sistema operativo:
# Ejecutar un comando simple
exit_code = os.system('echo "Hola desde el sistema"')
print(f"Código de salida: {exit_code}")
# Para capturar la salida del comando (recomendado en Python moderno)
import subprocess
resultado = subprocess.run(['ls', '-la'], capture_output=True, text=True)
print(f"Salida del comando:\n{resultado.stdout}")
Aunque os.system()
está disponible, se recomienda usar el módulo subprocess
para un control más preciso sobre la ejecución de comandos externos.
Información de archivos
Puedes obtener metadatos de archivos:
# Comprobar si un archivo existe
if os.path.exists('archivo.txt'):
# Obtener el tamaño del archivo
tamaño = os.path.getsize('archivo.txt')
print(f"Tamaño: {tamaño} bytes")
# Obtener la última modificación (timestamp)
tiempo_mod = os.path.getmtime('archivo.txt')
# Convertir timestamp a fecha legible
import datetime
fecha_mod = datetime.datetime.fromtimestamp(tiempo_mod)
print(f"Última modificación: {fecha_mod}")
# Comprobar permisos
es_legible = os.access('archivo.txt', os.R_OK)
es_escribible = os.access('archivo.txt', os.W_OK)
es_ejecutable = os.access('archivo.txt', os.X_OK)
print(f"Permisos - Lectura: {es_legible}, Escritura: {es_escribible}, Ejecución: {es_ejecutable}")
Las funciones de os.path
y os.access()
te permiten verificar la existencia y permisos de archivos antes de intentar operaciones que podrían fallar.
Operaciones con archivos
Aunque Python tiene funciones integradas para manejar archivos, el módulo os
proporciona operaciones a nivel del sistema:
# Renombrar un archivo
os.rename('archivo_viejo.txt', 'archivo_nuevo.txt')
# Mover un archivo
os.replace('archivo.txt', 'subdirectorio/archivo.txt')
# Eliminar un archivo
os.remove('archivo_a_eliminar.txt')
# Crear un enlace simbólico (solo sistemas Unix/Linux/Mac)
if os.name == 'posix':
os.symlink('archivo_original.txt', 'enlace.txt')
Estas operaciones te permiten manipular archivos a nivel del sistema de archivos sin necesidad de abrirlos para lectura o escritura.
Ejemplo práctico: Análisis de espacio en disco
Un caso de uso común es analizar el espacio utilizado en un directorio:
def analizar_directorio(ruta):
"""Analiza el espacio usado por un directorio y sus subdirectorios."""
tamaño_total = 0
archivos_count = 0
dirs_count = 0
for raiz, dirs, archivos in os.walk(ruta):
dirs_count += len(dirs)
archivos_count += len(archivos)
for archivo in archivos:
ruta_completa = os.path.join(raiz, archivo)
# Ignorar enlaces simbólicos para evitar conteo duplicado
if not os.path.islink(ruta_completa):
tamaño_total += os.path.getsize(ruta_completa)
# Convertir a MB para mejor legibilidad
tamaño_mb = tamaño_total / (1024 * 1024)
return {
'tamaño_mb': round(tamaño_mb, 2),
'archivos': archivos_count,
'directorios': dirs_count
}
# Uso
resultado = analizar_directorio('.')
print(f"Espacio utilizado: {resultado['tamaño_mb']} MB")
print(f"Archivos: {resultado['archivos']}")
print(f"Directorios: {resultado['directorios']}")
La función os.walk()
es extremadamente útil para recorrer recursivamente una estructura de directorios, permitiéndote procesar todos los archivos y subdirectorios.
Identificación de archivos especiales
En sistemas Unix/Linux, puedes identificar tipos especiales de archivos:
# Solo en sistemas Unix/Linux
if os.name == 'posix':
# Comprobar si es un dispositivo
es_dispositivo = os.path.isblk('/dev/sda') or os.path.ischr('/dev/tty')
# Comprobar si es un socket
es_socket = os.path.issock('/var/run/docker.sock')
# Comprobar si es un FIFO (named pipe)
es_fifo = os.path.isfifo('/tmp/mi_pipe')
Estas funciones te permiten trabajar con archivos especiales del sistema, lo que es útil para aplicaciones que necesitan interactuar con dispositivos o mecanismos de comunicación entre procesos.
El módulo os
proporciona una interfaz potente para interactuar con el sistema operativo, permitiéndote crear aplicaciones que puedan gestionar archivos, directorios y procesos de manera eficiente y compatible con múltiples plataformas.
Manipulación de rutas
El manejo eficiente de rutas de archivos y directorios es una tarea fundamental en cualquier aplicación que interactúe con el sistema de archivos. Python, a través del submódulo os.path
y las funciones relacionadas del módulo os
, proporciona herramientas robustas para manipular rutas de manera independiente de la plataforma.
La manipulación de rutas en Python debe considerar las diferencias entre sistemas operativos. Por ejemplo, Windows utiliza la barra invertida (\
) como separador de directorios, mientras que Unix/Linux/macOS utilizan la barra normal (/
). El módulo os.path
abstrae estas diferencias, permitiéndote escribir código portable entre diferentes sistemas.
Construcción de rutas
Una de las operaciones más comunes es la construcción de rutas a partir de componentes:
import os
# Unir componentes de ruta (independiente del sistema operativo)
ruta = os.path.join('directorio', 'subdirectorio', 'archivo.txt')
print(ruta) # En Windows: 'directorio\subdirectorio\archivo.txt'
# En Unix/Linux: 'directorio/subdirectorio/archivo.txt'
La función os.path.join()
es crucial para crear rutas compatibles con el sistema operativo actual, ya que utiliza automáticamente el separador correcto.
Para construir rutas más complejas, puedes combinar varios componentes:
# Construir ruta a partir del directorio base
directorio_base = os.path.expanduser('~') # Expande ~ a la ruta del home del usuario
ruta_documentos = os.path.join(directorio_base, 'Documentos', 'proyecto')
print(f"Ruta a documentos: {ruta_documentos}")
# Construir ruta relativa al directorio actual
ruta_relativa = os.path.join('..', 'datos', 'archivo.csv')
print(f"Ruta relativa: {ruta_relativa}")
La función os.path.expanduser()
es especialmente útil para acceder al directorio home del usuario de manera independiente de la plataforma.
Normalización y resolución de rutas
Las rutas pueden contener elementos como .
(directorio actual), ..
(directorio padre) o múltiples separadores que es conveniente normalizar:
# Normalizar una ruta (eliminar elementos redundantes)
ruta_desordenada = 'proyecto/./subdir/../datos/archivo.txt'
ruta_normalizada = os.path.normpath(ruta_desordenada)
print(f"Ruta normalizada: {ruta_normalizada}") # 'proyecto/datos/archivo.txt'
# Convertir ruta relativa a absoluta
ruta_relativa = 'datos/archivo.csv'
ruta_absoluta = os.path.abspath(ruta_relativa)
print(f"Ruta absoluta: {ruta_absoluta}")
# Resolver enlaces simbólicos (en sistemas Unix/Linux/Mac)
if os.name == 'posix':
ruta_real = os.path.realpath('enlace_simbolico.txt')
print(f"Ruta real: {ruta_real}")
La función os.path.normpath()
simplifica las rutas eliminando elementos redundantes, mientras que os.path.abspath()
convierte rutas relativas en absolutas basándose en el directorio de trabajo actual.
Descomposición de rutas
A menudo necesitamos extraer componentes específicos de una ruta:
ruta_completa = '/home/usuario/proyectos/app/datos/archivo.csv'
# Obtener el directorio y el nombre del archivo
directorio, archivo = os.path.split(ruta_completa)
print(f"Directorio: {directorio}") # '/home/usuario/proyectos/app/datos'
print(f"Archivo: {archivo}") # 'archivo.csv'
# Obtener el nombre base y la extensión
nombre_base, extension = os.path.splitext(archivo)
print(f"Nombre base: {nombre_base}") # 'archivo'
print(f"Extensión: {extension}") # '.csv'
# Obtener solo el nombre del directorio final
directorio_final = os.path.basename(directorio)
print(f"Directorio final: {directorio_final}") # 'datos'
# Obtener el directorio padre
directorio_padre = os.path.dirname(directorio)
print(f"Directorio padre: {directorio_padre}") # '/home/usuario/proyectos/app'
Estas funciones de descomposición son esenciales para procesar rutas y extraer información relevante sin tener que recurrir a manipulaciones de cadenas que podrían no ser compatibles entre plataformas.
Validación y comprobación de rutas
Antes de realizar operaciones con archivos, es importante verificar la existencia y naturaleza de las rutas:
ruta_a_verificar = '/ruta/a/algo'
# Comprobar si la ruta existe
existe = os.path.exists(ruta_a_verificar)
print(f"¿Existe la ruta? {existe}")
# Comprobar si es un archivo
es_archivo = os.path.isfile(ruta_a_verificar)
print(f"¿Es un archivo? {es_archivo}")
# Comprobar si es un directorio
es_directorio = os.path.isdir(ruta_a_verificar)
print(f"¿Es un directorio? {es_directorio}")
# Comprobar si es una ruta absoluta
es_absoluta = os.path.isabs(ruta_a_verificar)
print(f"¿Es una ruta absoluta? {es_absoluta}")
# Comprobar si dos rutas se refieren al mismo archivo
mismo_archivo = os.path.samefile('/ruta/a/archivo', './archivo')
print(f"¿Son el mismo archivo? {mismo_archivo}")
Estas funciones de validación te permiten tomar decisiones basadas en la naturaleza de las rutas antes de intentar operaciones que podrían fallar.
Rutas relativas entre directorios
A veces necesitamos calcular la ruta relativa entre dos directorios:
# Calcular la ruta relativa desde un directorio a otro
desde_dir = '/home/usuario/proyectos'
hasta_dir = '/home/usuario/documentos/datos'
ruta_relativa = os.path.relpath(hasta_dir, desde_dir)
print(f"Ruta relativa: {ruta_relativa}") # '../documentos/datos'
La función os.path.relpath()
es útil para crear referencias relativas entre directorios, lo que puede ser importante en aplicaciones que necesitan mantener relaciones entre diferentes ubicaciones del sistema de archivos.
Manipulación de rutas con pathlib (Python 3.4+)
A partir de Python 3.4, la biblioteca estándar incluye el módulo pathlib
, que ofrece una interfaz orientada a objetos para manipular rutas:
from pathlib import Path
# Crear un objeto Path
ruta = Path('/home/usuario/documentos/archivo.txt')
# Acceder a componentes
print(f"Directorio padre: {ruta.parent}")
print(f"Nombre del archivo: {ruta.name}")
print(f"Extensión: {ruta.suffix}")
print(f"Nombre sin extensión: {ruta.stem}")
# Unir rutas
nueva_ruta = ruta.parent / 'datos' / 'nuevo.csv'
print(f"Nueva ruta: {nueva_ruta}")
# Resolver rutas relativas
ruta_actual = Path('.')
ruta_absoluta = ruta_actual.absolute()
print(f"Ruta absoluta: {ruta_absoluta}")
# Iterar sobre archivos en un directorio
for archivo in Path('.').glob('*.py'):
print(f"Archivo Python: {archivo}")
El módulo pathlib
proporciona una interfaz más intuitiva y moderna para trabajar con rutas, permitiendo operaciones como la concatenación de rutas mediante el operador /
y métodos para buscar archivos con patrones.
Ejemplo práctico: Organización de archivos por extensión
Un caso de uso común es organizar archivos en directorios según su extensión:
def organizar_por_extension(directorio_origen):
"""Organiza los archivos de un directorio en subdirectorios según su extensión."""
# Asegurar que el directorio existe
if not os.path.exists(directorio_origen) or not os.path.isdir(directorio_origen):
print(f"El directorio {directorio_origen} no existe o no es un directorio")
return
# Procesar cada archivo en el directorio
for archivo in os.listdir(directorio_origen):
ruta_completa = os.path.join(directorio_origen, archivo)
# Ignorar directorios
if os.path.isdir(ruta_completa):
continue
# Obtener la extensión (sin el punto)
_, extension = os.path.splitext(archivo)
extension = extension[1:].lower() # Eliminar el punto y convertir a minúsculas
if not extension:
extension = 'sin_extension'
# Crear el directorio de destino si no existe
directorio_destino = os.path.join(directorio_origen, extension)
if not os.path.exists(directorio_destino):
os.mkdir(directorio_destino)
# Mover el archivo
destino = os.path.join(directorio_destino, archivo)
os.rename(ruta_completa, destino)
print(f"Movido: {archivo} -> {extension}/{archivo}")
# Uso
organizar_por_extension('/ruta/a/mis/descargas')
Este ejemplo demuestra cómo combinar varias funciones de manipulación de rutas para implementar una utilidad práctica que organiza archivos según su extensión.
Compatibilidad entre sistemas operativos
Al trabajar con rutas en diferentes sistemas operativos, es importante considerar algunas diferencias:
# Determinar el separador de rutas del sistema actual
print(f"Separador de rutas: {os.path.sep}") # '\' en Windows, '/' en Unix/Linux
# Determinar el separador de rutas en listas de rutas (PATH)
print(f"Separador de PATH: {os.path.pathsep}") # ';' en Windows, ':' en Unix/Linux
# Convertir rutas entre formatos
ruta_windows = r'C:\Users\usuario\documentos\archivo.txt'
ruta_unix = ruta_windows.replace('\\', '/')
print(f"Ruta en formato Unix: {ruta_unix}")
# Usar os.path.normcase para normalizar según el sistema
ruta_normalizada = os.path.normcase(ruta_windows)
print(f"Ruta normalizada para el sistema actual: {ruta_normalizada}")
Conocer estas diferencias te permite escribir código que funcione correctamente en múltiples plataformas, evitando problemas comunes como rutas mal formadas o incompatibles.
La manipulación eficiente de rutas es fundamental para crear aplicaciones robustas que interactúen con el sistema de archivos. El módulo os.path
y la biblioteca pathlib
proporcionan las herramientas necesarias para trabajar con rutas de manera segura y compatible entre diferentes sistemas operativos.
Manejo de procesos
El módulo os
de Python no solo permite interactuar con archivos y directorios, sino que también proporciona herramientas para gestionar procesos del sistema operativo. Esta capacidad es fundamental cuando necesitamos ejecutar programas externos, controlar su ejecución o comunicarnos con ellos desde nuestras aplicaciones Python.
La gestión de procesos varía significativamente entre sistemas operativos, pero el módulo os
abstrae muchas de estas diferencias, permitiéndonos escribir código que funcione en múltiples plataformas.
Creación de procesos
La forma más básica de crear un proceso es mediante la función os.system()
, que ejecuta un comando como si lo hubiéramos escrito en la terminal:
import os
# Ejecutar un comando simple
estado = os.system('echo "Ejecutando un comando externo"')
print(f"Estado de salida: {estado}")
Sin embargo, os.system()
tiene limitaciones importantes: no permite capturar fácilmente la salida del comando y ofrece poco control sobre el proceso. Por eso, Python proporciona alternativas más potentes.
La función os.spawn
Para un mayor control sobre la creación de procesos, Python ofrece la familia de funciones os.spawn*()
:
# En sistemas Windows
if os.name == 'nt':
# Ejecutar el Bloc de notas de Windows
pid = os.spawnl(os.P_NOWAIT, 'notepad.exe', 'notepad.exe', 'documento.txt')
print(f"Proceso iniciado con PID: {pid}")
Las funciones spawn
tienen varias variantes que permiten controlar si el proceso padre espera (P_WAIT
) o no (P_NOWAIT
) a que el hijo termine, y cómo se pasan los argumentos.
Ejecutar procesos con subprocess
Aunque el módulo os
ofrece funcionalidades básicas para manejar procesos, el módulo subprocess
proporciona una interfaz más completa y flexible:
import subprocess
# Ejecutar un comando y capturar su salida
resultado = subprocess.run(['ls', '-la'], capture_output=True, text=True)
if resultado.returncode == 0:
print("Comando ejecutado con éxito")
print(f"Salida:\n{resultado.stdout}")
else:
print(f"Error al ejecutar el comando: {resultado.stderr}")
El método subprocess.run()
es la forma recomendada para ejecutar comandos externos en Python moderno. Permite:
- Capturar la salida estándar y error estándar
- Proporcionar entrada al proceso
- Establecer variables de entorno
- Controlar el directorio de trabajo
Comunicación con procesos
Para casos más complejos donde necesitamos comunicación bidireccional con un proceso, podemos usar subprocess.Popen
:
# Iniciar un proceso con tuberías para entrada/salida
proceso = subprocess.Popen(
['python', '-c', 'import sys; print(sys.stdin.read().upper())'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
)
# Enviar datos al proceso
texto_entrada = "Hola, proceso hijo!\n"
stdout_data, _ = proceso.communicate(texto_entrada)
print(f"Respuesta del proceso: {stdout_data}")
El método communicate()
permite enviar datos a la entrada estándar del proceso y recibir su salida, facilitando la comunicación bidireccional.
Obtener información de procesos
El módulo os
proporciona funciones para obtener información sobre el proceso actual:
# Obtener el ID del proceso actual
pid_actual = os.getpid()
print(f"PID del proceso actual: {pid_actual}")
# Obtener el ID del proceso padre
ppid = os.getppid()
print(f"PID del proceso padre: {ppid}")
# Obtener el usuario efectivo (solo en sistemas Unix)
if os.name == 'posix':
uid = os.geteuid()
print(f"UID efectivo: {uid}")
Esta información es útil para depuración o para implementar lógica que dependa de la identidad del proceso.
Control de procesos
Python permite controlar procesos en ejecución, aunque algunas funcionalidades están limitadas a sistemas tipo Unix:
# Enviar una señal a un proceso (solo en sistemas Unix)
if os.name == 'posix':
import signal
# Definir un manejador de señal
def manejador_señal(signum, frame):
print(f"Recibida señal: {signum}")
# Registrar el manejador para SIGINT (Ctrl+C)
signal.signal(signal.SIGINT, manejador_señal)
print("Presiona Ctrl+C para enviar SIGINT...")
# Esperar a recibir la señal
try:
signal.pause()
except KeyboardInterrupt:
print("Programa interrumpido")
Las señales son un mecanismo de comunicación entre procesos en sistemas Unix que permiten notificar eventos o solicitar acciones específicas.
Terminación de procesos
Para terminar un proceso, podemos usar os.kill()
en sistemas Unix:
# Terminar un proceso (solo en sistemas Unix)
if os.name == 'posix':
import signal
# Crear un proceso hijo
pid = os.fork()
if pid == 0: # Proceso hijo
print(f"Proceso hijo iniciado con PID: {os.getpid()}")
# Simular trabajo
import time
time.sleep(10)
else: # Proceso padre
print(f"Proceso padre. Hijo PID: {pid}")
time.sleep(2) # Esperar un poco
# Terminar el proceso hijo
print(f"Terminando el proceso hijo {pid}")
os.kill(pid, signal.SIGTERM)
# Esperar a que el hijo termine
_, estado = os.waitpid(pid, 0)
print(f"Proceso hijo terminado con estado: {estado}")
La función os.waitpid()
permite al proceso padre esperar a que un proceso hijo específico termine y obtener su estado de salida.
Ejecución en segundo plano
Para ejecutar procesos en segundo plano que continúen incluso después de que el programa Python termine:
# Iniciar un proceso desvinculado (solo en sistemas Unix)
if os.name == 'posix':
# Usar nohup para que el proceso continúe después de cerrar la terminal
subprocess.Popen(['nohup', 'python', '-c', 'import time; open("background.log", "w").write("Ejecutando..."); time.sleep(60)'],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True)
print("Proceso iniciado en segundo plano")
El parámetro start_new_session=True
crea un nuevo grupo de procesos, lo que permite que el proceso hijo se ejecute independientemente del padre.
Monitoreo de procesos hijos
En aplicaciones más complejas, podemos necesitar monitorear varios procesos hijos:
def ejecutar_tareas_paralelas(comandos):
"""Ejecuta múltiples comandos en paralelo y espera a que todos terminen."""
procesos = []
# Iniciar todos los procesos
for cmd in comandos:
proceso = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
procesos.append((proceso, cmd))
# Esperar y recopilar resultados
resultados = []
for proceso, cmd in procesos:
stdout, stderr = proceso.communicate()
resultados.append({
'comando': cmd,
'exitoso': proceso.returncode == 0,
'salida': stdout.decode('utf-8'),
'error': stderr.decode('utf-8')
})
return resultados
# Ejemplo de uso
tareas = [
['echo', 'Tarea 1'],
['ls', '-la'],
['echo', 'Tarea 3']
]
resultados = ejecutar_tareas_paralelas(tareas)
for res in resultados:
estado = "✓" if res['exitoso'] else "✗"
print(f"{estado} {' '.join(res['comando'])}")
if res['salida']:
print(f" Salida: {res['salida'].strip()}")
Esta técnica es útil para paralelizar tareas y aprovechar mejor los recursos del sistema, especialmente en aplicaciones que necesitan procesar grandes volúmenes de datos.
Ejemplo práctico: Supervisor de procesos
Un caso de uso común es crear un supervisor que reinicie procesos si fallan:
def supervisor(comando, max_intentos=3):
"""Supervisa un proceso y lo reinicia si falla."""
intentos = 0
while intentos < max_intentos:
print(f"Iniciando proceso (intento {intentos + 1}/{max_intentos})...")
# Iniciar el proceso
inicio = time.time()
proceso = subprocess.Popen(comando)
# Esperar a que termine
codigo_salida = proceso.wait()
duracion = time.time() - inicio
if codigo_salida == 0:
print(f"Proceso completado correctamente después de {duracion:.2f} segundos")
return True
else:
print(f"Proceso falló con código {codigo_salida} después de {duracion:.2f} segundos")
intentos += 1
time.sleep(2) # Esperar antes de reintentar
print(f"El proceso falló después de {max_intentos} intentos")
return False
# Ejemplo de uso
import time
supervisor(['python', '-c', 'import random, time; time.sleep(2); exit(0 if random.random() > 0.7 else 1)'])
Este supervisor es útil para aplicaciones críticas que deben mantenerse en funcionamiento a pesar de fallos ocasionales.
Consideraciones de seguridad
Al ejecutar comandos externos, es importante tener en cuenta la seguridad:
# INSEGURO: Vulnerabilidad de inyección de comandos
usuario_input = input("Ingrese un archivo a buscar: ")
os.system(f"find / -name {usuario_input}") # ¡PELIGROSO!
# SEGURO: Usar subprocess con argumentos separados
archivo = input("Ingrese un archivo a buscar: ")
subprocess.run(["find", "/", "-name", archivo])
Siempre que sea posible, usa subprocess.run()
con una lista de argumentos en lugar de construir cadenas de comandos, para evitar vulnerabilidades de inyección.
El manejo de procesos en Python a través del módulo os
y subprocess
proporciona herramientas potentes para interactuar con el sistema operativo, permitiendo crear aplicaciones que puedan ejecutar y controlar otros programas de manera eficiente y segura.
Variables de entorno
Las variables de entorno son valores dinámicos que afectan el comportamiento de los procesos en ejecución en un sistema operativo. Funcionan como un mecanismo de configuración global que permite a las aplicaciones obtener información del entorno en el que se ejecutan sin necesidad de codificarla directamente en el programa.
En Python, el módulo os
proporciona una interfaz completa para trabajar con estas variables, permitiéndote acceder, modificar y gestionar el entorno de ejecución de tus aplicaciones de manera eficiente.
Acceso a variables de entorno
El diccionario os.environ
es la principal herramienta para acceder a las variables de entorno del sistema:
import os
# Listar todas las variables de entorno disponibles
for clave, valor in os.environ.items():
print(f"{clave}: {valor}")
Para acceder a una variable específica, puedes usar la sintaxis de diccionario:
# Acceder a variables de entorno comunes
python_path = os.environ.get('PYTHONPATH')
usuario = os.environ.get('USER') # En Unix/Linux
# o en Windows:
usuario_windows = os.environ.get('USERNAME')
print(f"Usuario actual: {usuario or usuario_windows}")
El método get()
es preferible a la indexación directa (os.environ['VARIABLE']
), ya que devuelve None
si la variable no existe en lugar de lanzar una excepción KeyError
.
Modificación de variables de entorno
Puedes modificar variables de entorno para el proceso actual y sus procesos hijos:
# Establecer una nueva variable de entorno
os.environ['APP_MODE'] = 'development'
# Modificar una variable existente
os.environ['PATH'] = f"{os.environ.get('PATH', '')}:/ruta/adicional"
# Eliminar una variable de entorno
if 'TEMPORAL_VAR' in os.environ:
del os.environ['TEMPORAL_VAR']
Es importante entender que estos cambios solo afectan al proceso actual y sus procesos hijos. No modifican permanentemente las variables de entorno del sistema operativo.
Uso de variables de entorno para configuración
Una práctica común es utilizar variables de entorno para configurar aplicaciones:
# Configuración basada en variables de entorno
def obtener_configuracion():
"""Obtiene la configuración de la aplicación desde variables de entorno."""
config = {
'debug': os.environ.get('APP_DEBUG', 'False').lower() in ('true', '1', 't'),
'host': os.environ.get('APP_HOST', 'localhost'),
'port': int(os.environ.get('APP_PORT', '8000')),
'db_url': os.environ.get('DATABASE_URL', 'sqlite:///app.db'),
'log_level': os.environ.get('LOG_LEVEL', 'INFO').upper(),
}
return config
# Uso
config = obtener_configuracion()
print(f"Ejecutando en modo debug: {config['debug']}")
print(f"Servidor: {config['host']}:{config['port']}")
Este enfoque permite cambiar el comportamiento de la aplicación sin modificar el código, lo que es especialmente útil en entornos de contenedores o despliegues en la nube.
Variables de entorno temporales para subprocesos
Cuando ejecutas un subproceso, puedes proporcionarle un entorno específico:
import subprocess
# Crear un entorno personalizado para un subproceso
env_personalizado = os.environ.copy() # Copia el entorno actual
env_personalizado['DEBUG'] = '1' # Añade o modifica variables
# Ejecutar un proceso con el entorno personalizado
resultado = subprocess.run(
['python', '-c', 'import os; print(f"Debug: {os.environ.get(\'DEBUG\')}")'],
env=env_personalizado,
text=True,
capture_output=True
)
print(f"Salida del subproceso: {resultado.stdout.strip()}")
La función os.environ.copy()
es crucial aquí, ya que crea una copia del entorno actual que puedes modificar sin afectar al proceso principal.
Carga de variables desde archivos .env
Un patrón común en el desarrollo moderno es almacenar configuraciones en archivos .env
:
def cargar_env(ruta_archivo='.env'):
"""Carga variables de entorno desde un archivo .env"""
if not os.path.exists(ruta_archivo):
return False
with open(ruta_archivo, 'r') as archivo:
for linea in archivo:
linea = linea.strip()
if not linea or linea.startswith('#'):
continue
clave, valor = linea.split('=', 1)
os.environ[clave.strip()] = valor.strip()
return True
# Uso
if cargar_env():
print("Variables de entorno cargadas desde .env")
else:
print("Archivo .env no encontrado, usando valores predeterminados")
Aunque esta implementación básica funciona, para proyectos reales se recomienda usar bibliotecas como python-dotenv o python-decouple que manejan casos más complejos como comillas, espacios y valores multilínea.
Expansión de variables en rutas
Las variables de entorno son útiles para construir rutas dinámicas:
# Expandir variables de entorno en rutas
ruta_con_variables = '$HOME/proyectos/${APP_NAME}/datos'
# Expandir las variables en la ruta
ruta_expandida = os.path.expandvars(ruta_con_variables)
print(f"Ruta expandida: {ruta_expandida}")
La función os.path.expandvars()
reemplaza referencias a variables de entorno (con formato $VARIABLE
o ${VARIABLE}
) con sus valores correspondientes.
Gestión de variables de entorno en diferentes plataformas
Las variables de entorno pueden variar entre sistemas operativos:
# Detectar el directorio home del usuario de forma multiplataforma
home_dir = os.environ.get('HOME') # Unix/Linux/Mac
if home_dir is None: # En Windows
home_dir = os.environ.get('USERPROFILE')
print(f"Directorio home: {home_dir}")
# Alternativa usando os.path.expanduser
home_dir_alt = os.path.expanduser('~')
print(f"Directorio home (alternativa): {home_dir_alt}")
La función os.path.expanduser('~')
es una forma multiplataforma de obtener el directorio home del usuario, independientemente del sistema operativo.
Variables de entorno para seguridad
Las variables de entorno son ideales para almacenar información sensible:
# Obtener credenciales desde variables de entorno
def obtener_credenciales():
"""Obtiene credenciales de API desde variables de entorno."""
api_key = os.environ.get('API_KEY')
api_secret = os.environ.get('API_SECRET')
if not api_key or not api_secret:
raise ValueError("Credenciales de API no configuradas en variables de entorno")
return {
'api_key': api_key,
'api_secret': api_secret
}
# Uso con manejo de errores
try:
credenciales = obtener_credenciales()
print("Credenciales obtenidas correctamente")
except ValueError as e:
print(f"Error: {e}")
print("Configure API_KEY y API_SECRET en las variables de entorno")
Este enfoque evita incluir información sensible directamente en el código fuente, reduciendo riesgos de seguridad como la exposición accidental de credenciales en repositorios de código.
Ejemplo práctico: Configuración basada en entorno
Un patrón común es tener diferentes configuraciones según el entorno de ejecución:
def configurar_app():
"""Configura la aplicación según el entorno."""
# Determinar el entorno
entorno = os.environ.get('APP_ENV', 'development').lower()
# Configuración base
config = {
'debug': False,
'timeout': 30,
'max_connections': 100,
'log_level': 'INFO'
}
# Ajustar según el entorno
if entorno == 'development':
config.update({
'debug': True,
'log_level': 'DEBUG',
'db_url': 'sqlite:///dev.db'
})
elif entorno == 'testing':
config.update({
'debug': True,
'db_url': 'sqlite:///:memory:',
'timeout': 5
})
elif entorno == 'production':
# En producción, todas las configuraciones críticas deben venir de variables de entorno
config.update({
'db_url': os.environ.get('DATABASE_URL'),
'secret_key': os.environ.get('SECRET_KEY'),
'max_connections': int(os.environ.get('MAX_CONNECTIONS', '500'))
})
# Verificar configuraciones críticas
if not config['db_url'] or not config['secret_key']:
raise ValueError("Faltan variables de entorno críticas para producción")
return config
# Uso
try:
config = configurar_app()
print(f"Aplicación configurada para entorno: {os.environ.get('APP_ENV', 'development')}")
print(f"Debug: {config['debug']}")
print(f"Nivel de log: {config['log_level']}")
except ValueError as e:
print(f"Error de configuración: {e}")
Este patrón permite mantener una configuración flexible que se adapta automáticamente al entorno de ejecución, facilitando el despliegue en diferentes escenarios.
Herencia de variables de entorno en procesos hijos
Cuando creas procesos hijos, estos heredan las variables de entorno del proceso padre:
# Demostración de herencia de variables de entorno
import subprocess
# Establecer una variable en el proceso actual
os.environ['MENSAJE'] = 'Hola desde el proceso padre'
# Ejecutar un proceso hijo que accede a la variable
resultado = subprocess.run(
['python', '-c', 'import os; print(f"Variable heredada: {os.environ.get(\'MENSAJE\')}")'],
text=True,
capture_output=True
)
print(f"Salida del proceso hijo: {resultado.stdout.strip()}")
Esta característica permite propagar configuraciones a través de la jerarquía de procesos sin necesidad de pasarlas explícitamente como argumentos.
Las variables de entorno proporcionan un mecanismo flexible y potente para configurar aplicaciones y compartir información entre procesos. El módulo os
de Python ofrece todas las herramientas necesarias para trabajar con ellas de manera eficiente, permitiéndote crear aplicaciones adaptables a diferentes entornos de ejecución.
Otros ejercicios de programación de Python
Evalúa tus conocimientos de esta lección Módulo os con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Módulo math
Reto herencia
Excepciones
Introducción a Python
Reto variables
Funciones Python
Reto funciones
Módulo datetime
Reto acumulación
Reto estructuras condicionales
Polimorfismo
Módulo os
Reto métodos dunder
Diccionarios
Reto clases y objetos
Reto operadores
Operadores
Estructuras de control
Funciones lambda
Reto diccionarios
Reto función lambda
Encapsulación
Reto coleciones
Reto funciones auxiliares
Crear módulos y paquetes
Módulo datetime
Excepciones
Operadores
Diccionarios
Reto map, filter
Reto tuplas
Proyecto gestor de tareas CRUD
Tuplas
Variables
Tipos de datos
Conjuntos
Reto mixins
Módulo csv
Módulo json
Herencia
Análisis de datos de ventas con Pandas
Reto fechas y tiempo
Reto estructuras de iteración
Funciones
Reto comprehensions
Variables
Reto serialización
Módulo csv
Reto polimorfismo
Polimorfismo
Clases y objetos
Reto encapsulación
Estructuras de control
Importar módulos y paquetes
Módulo math
Funciones lambda
Reto excepciones
Listas
Reto archivos
Encapsulación
Reto conjuntos
Clases y objetos
Instalación de Python y creación de proyecto
Reto listas
Tipos de datos
Crear módulos y paquetes
Tuplas
Herencia
Reto acceso a sistema
Proyecto sintaxis calculadora
Importar módulos y paquetes
Clases y objetos
Módulo os
Listas
Conjuntos
Reto tipos de datos
Reto matemáticas
Módulo json
Todas las lecciones de Python
Accede a todas las lecciones de Python y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Python
Introducción
Instalación Y Creación De Proyecto
Introducción
Tema 2: Tipos De Datos, Variables Y Operadores
Introducción
Instalación De Python
Introducción
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Estructuras Control Iterativo
Sintaxis
Estructuras Control Condicional
Sintaxis
Testing Con Pytest
Sintaxis
Listas
Estructuras De Datos
Tuplas
Estructuras De Datos
Diccionarios
Estructuras De Datos
Conjuntos
Estructuras De Datos
Comprehensions
Estructuras De Datos
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Mixins Y Herencia Múltiple
Programación Orientada A Objetos
Métodos Especiales (Dunder Methods)
Programación Orientada A Objetos
Composición De Clases
Programación Orientada A Objetos
Funciones Lambda
Programación Funcional
Aplicación Parcial
Programación Funcional
Entrada Y Salida, Manejo De Archivos
Programación Funcional
Decoradores
Programación Funcional
Generadores
Programación Funcional
Paradigma Funcional
Programación Funcional
Composición De Funciones
Programación Funcional
Funciones Orden Superior Map Y Filter
Programación Funcional
Funciones Auxiliares
Programación Funcional
Reducción Y Acumulación
Programación Funcional
Archivos Comprimidos
Entrada Y Salida Io
Entrada Y Salida Avanzada
Entrada Y Salida Io
Archivos Temporales
Entrada Y Salida Io
Contexto With
Entrada Y Salida Io
Módulo Csv
Biblioteca Estándar
Módulo Json
Biblioteca Estándar
Módulo Datetime
Biblioteca Estándar
Módulo Math
Biblioteca Estándar
Módulo Os
Biblioteca Estándar
Módulo Re
Biblioteca Estándar
Módulo Random
Biblioteca Estándar
Módulo Time
Biblioteca Estándar
Módulo Collections
Biblioteca Estándar
Módulo Sys
Biblioteca Estándar
Módulo Statistics
Biblioteca Estándar
Módulo Pickle
Biblioteca Estándar
Módulo Pathlib
Biblioteca Estándar
Importar Módulos Y Paquetes
Paquetes Y Módulos
Crear Módulos Y Paquetes
Paquetes Y Módulos
Entornos Virtuales (Virtualenv, Venv)
Entorno Y Dependencias
Gestión De Dependencias (Pip, Requirements.txt)
Entorno Y Dependencias
Python-dotenv Y Variables De Entorno
Entorno Y Dependencias
Acceso A Datos Con Mysql, Pymongo Y Pandas
Acceso A Bases De Datos
Acceso A Mongodb Con Pymongo
Acceso A Bases De Datos
Acceso A Mysql Con Mysql Connector
Acceso A Bases De Datos
Novedades Python 3.13
Características Modernas
Operador Walrus
Características Modernas
Pattern Matching
Características Modernas
Instalación Beautiful Soup
Web Scraping
Sintaxis General De Beautiful Soup
Web Scraping
Tipos De Selectores
Web Scraping
Web Scraping De Html
Web Scraping
Web Scraping Para Ciencia De Datos
Web Scraping
Autenticación Y Acceso A Recursos Protegidos
Web Scraping
Combinación De Selenium Con Beautiful Soup
Web Scraping
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender cómo utilizar el módulo os para interactuar con el sistema operativo de forma multiplataforma.
- Aprender a manipular rutas de archivos y directorios de manera segura y portable.
- Gestionar procesos del sistema operativo, incluyendo creación, comunicación y control.
- Acceder, modificar y utilizar variables de entorno para configurar aplicaciones.
- Aplicar buenas prácticas y técnicas para la ejecución segura y eficiente de comandos y procesos externos.