Geekflare recibe el apoyo de nuestra audiencia. Podemos ganar comisiones de afiliación de los enlaces de compra en este sitio.
En Desarrollo Última actualización: 25 de septiembre de 2023
Compartir en:
Escáner de seguridad de aplicaciones web Invicti - la única solución que ofrece verificación automática de vulnerabilidades con Proof-Based Scanning™.

Los subprocesos le permiten interactuar a un nivel totalmente nuevo con el Sistema Operativo.

Nuestro ordenador ejecuta subprocesos todo el tiempo. De hecho, sólo con leer este artículo, usted está ejecutando un montón de procesos como un gestor de red, o el propio navegador de Internet.

Lo bueno de esto es que cualquier acción que hagamos en nuestro ordenador implica invocar a un subproceso. Esto sigue siendo cierto incluso si estamos escribiendo un simple script "hola mundo" en python.

El concepto de subproceso puede parecer oscuro incluso si lleva un tiempo aprendiendo a programar. Este artículo profundizará en el concepto principal del subproceso y en cómo utilizar la biblioteca estándar de subprocesos de Python.

Al final de este tutorial, usted podrá:

  • Comprenderá el concepto de subproceso
  • Habrá aprendido los fundamentos de la biblioteca de subprocesos de Python
  • Habrá practicado sus conocimientos de Python con ejemplos útiles

Entremos en materia

El concepto de subproceso

En términos generales, un subproceso es un proceso informático creado por otro proceso.

Podemos pensar en un subproceso como un árbol, en el que cada proceso padre tiene procesos hijos ejecutándose detrás de él. Sé que esto puede resultar bastante confuso, pero veámoslo con un sencillo gráfico.

Hay varias formas de visualizar los procesos que se ejecutan en nuestro ordenador. Por ejemplo, en UNIX (Linux y MAC) tenemos htop, que es un visor de procesos interactivo.

Visor de procesos Htop

El modo árbol es la herramienta más útil para echar un vistazo a los subprocesos en ejecución. Podemos activarlo con F5.

Si echamos un vistazo de cerca a la sección de comandos, podemos darnos cuenta de la estructura de los procesos que se ejecutan en nuestro ordenador.

Estructura del proceso Htop
Todo comienza con /sbin/init que es el comando que inicia cada proceso en nuestro ordenador. Desde ese punto, podemos ver el inicio de otros procesos como xfce4-screenshoter y el xfce4-terminal (Que conduce a aún más subprocesos)

Echando un vistazo a Windows, tenemos el mítico administrador de tareas que resulta útil a la hora de matar esos programas que se cuelan en nuestra máquina.

Administrador de tareas de Windows

Ahora tenemos un concepto muy claro. Veamos cómo podemos implementar subprocesos en Python.

Subprocesos en Python

Un subproceso en Python es una tarea que un script python delega al Sistema Operativo (SO).

La biblioteca de subprocesos nos permite ejecutar y gestionar subprocesos directamente desde Python. Esto implica trabajar con la entrada estándar stdin, la salida estándar stdout y los códigos de retorno.

No tenemos que instalarla con PIP, ya que forma parte de la biblioteca estándar de Python .

Por lo tanto, podemos empezar a utilizar subprocesos en python con sólo importar el módulo.

import subprocess

# Usando el módulo ....

Nota: Para seguir este artículo debe tener Python 3.5

Para comprobar la versión de python que tiene actualmente, simplemente ejecute

❯ python --version
Python 3.9.5 # Mi resultado

En caso de que la versión de Python que obtenga sea la 2.x puede utilizar el siguiente comando

python3 --version

Siguiendo con el tema, la idea principal tras la librería de subprocesos es poder interactuar con el SO ejecutando los comandos que queramos, directamente desde el intérprete de Python.

Eso significa que podemos hacer lo que queramos, siempre y cuando nuestro SO nos lo permita (Y siempre y cuando no elimine su sistema de archivos raíz 😅).

Veamos cómo utilizarlo creando un sencillo script que liste los archivos del directorio actual.

Primera aplicación del subproceso

En primer lugar, vamos a crear un archivo list_dir.py. Este será el archivo donde experimentaremos listando archivos.

touch list_dir.py

Ahora abramos ese archivo y utilicemos el siguiente código.

import subproceso 

subproceso.run('ls')

En primer lugar, estamos importando el módulo subproceso, y luego utilizando la función ejecute que ejecuta, el comando que pasamos como argumento.

Esta función se introdujo en Python 3.5, como un atajo amigable a subproceso. Popen. La función subprocess.run nos permite ejecutar un comando y esperar a que termine, en contraste con Popen donde tenemos la opción de llamar a communicate más tarde.

Hablando de la salida del código, ls es un comando UNIX que lista los archivos del directorio en el que se encuentra. Por lo tanto, si ejecuta este comando, obtendrá una lista de los archivos presentes en el directorio actual.

❯ python list_dir.py
ejemplo.py LICENCIA list_dir.py README.md

Nota: Tenga en cuenta que si está en Windows, tendrá que utilizar comandos diferentes. Por ejemplo en lugar de usar "ls" puede utilizar "dir"

Esto puede parecer demasiado simple, y tiene razón. Usted quiere tener un acercamiento completo a todo el poder que el shell le brinda. Así que aprendamos a pasar argumentos al shell con subproceso.

Por ejemplo para listar también los archivos ocultos (Aquellos que comienzan con un punto), y también listar todos los metadatos de los archivos, escribimos el siguiente código.

import subprocess

# subprocess.run('ls') # Comando simple

subprocess.run('ls -la', shell=True)

Estamos ejecutando este comando como una cadena y utilizando el argumento concha. Esto significa que estamos invocando un shell al inicio de la ejecución de nuestro subproceso, y el argumento del comando es interpretado directamente por el shell.

Sin embargo, el uso de shell=Verdadero tiene muchos inconvenientes, y los peores son las posibles fugas de seguridad. Puede leer sobre ellas en la documentación oficial.

La mejor forma de pasar comandos a la función de ejecución es utilizar una lista donde lst[0] es el comando a llamar (ls en este caso) y lst[n] hijo los argumentos de ese comando.

Si lo hacemos así, nuestro código tendrá el siguiente aspecto.

import subprocess

# subprocess.run('ls') # Comando simple

# subprocess.run('ls -la', shell=True) # Comando peligroso

subprocess.run(['ls', '-la'])

Si queremos almacenar la salida estándar de un subproceso en una variable, podemos hacerlo estableciendo el argumento capture_output a true.

list_of_files = subprocess.run(['ls', '-la'], capture_output=True)

print(list_of_files.stdout)

❯ python list_dir.py 
b'total 36\ndrwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .\ndrwx------ 30 daniel daniel 4096 mayo 20 18:03 ..\n-rw-r--r-- 1 daniel daniel 55 mayo 20 20:18 example.py\ndrwxr-xr-x 8 daniel daniel 4096 mayo 20 17:31 .git\n-rw-r--r-- 1 daniel daniel 2160 mayo 17 22:23 .gitignore\n-rw-r--r-- 1 daniel daniel 271 mayo 20 19:53 internet_checker.py\n-rw-r--r-- 1 daniel daniel 1076 mayo 17 22:23 LICENSE\n-rw-r--r-- 1 daniel daniel 216 mayo 20 22:12 list_dir.py\n-rw-r--r-- 1 daniel daniel 22 mayo 17 22:23 README.md\n'

Para acceder a la salida de un proceso, utilizamos el atributo de instancia stdout.

En este caso, queremos almacenar la salida como una cadena, en lugar de bytes y podemos hacerlo estableciendo el argumento text como true.

list_of_files = subprocess.run(['ls', '-la'], capture_output=True, text=True)

print(list_of_files.stdout)

❯ python list_dir.py
total 36
drwxr-xr-x 3 daniel daniel 4096 may 20 21:08 .
drwx------ 30 daniel daniel 4096 mayo 20 18:03 ..
-rw-r--r-- 1 daniel daniel 55 mayo 20 20:18 example.py
drwxr-xr-x 8 daniel daniel 4096 mayo 20 17:31 .git
-rw-r--r-- 1 daniel daniel 2160 mayo 17 22:23 .gitignore
-rw-r--r-- 1 daniel daniel 271 mayo 20 19:53 internet_checker.py
-rw-r--r-- 1 daniel daniel 1076 mayo 17 22:23 LICENCIA
-rw-r--r-- 1 daniel daniel 227 mayo 20 22:14 list_dir.py
-rw-r--r-- 1 daniel daniel 22 mayo 17 22:23 README.md

Perfecto, ahora que conocemos los fundamentos de la biblioteca de subprocesos, es hora de pasar a algunos ejemplos de uso.

Ejemplos de uso de subproceso en Python

En esta sección, vamos a revisar algunos usos prácticos del subproceso de biblioteca. Puede consultarlos todos en este repositorio de Github.

Comprobador de programas

Uno de los principales usos de esta librería es la posibilidad de realizar operaciones sencillas con el sistema operativo.

Por ejemplo, un simple script que compruebe si un programa está instalado. En Linux, podemos hacer esto con el comando que .

'''Comprobador de programas con subproceso'''

import subproceso

programa = 'git'

proceso = subproceso. run(['which', programa], capture_output=True, text=True)

if proceso.returncode == 0: 
 print(f'El programa "{program}" está instalado')

 print(f'La ubicación del binario es: {process.stdout}')
else:
 print(f'Lo sentimos el {program} no está instalado')

 print(process.stderr)

Nota: En UNIX cuando un comando tiene éxito su código de estado es 0. De lo contrario, algo salió mal durante la ejecución

Como no estamos utilizando el argumento shell=Verdaderopodemos tomar la entrada del usuario de forma segura. Además, podemos comprobar si la entrada es un programa válido con un patrón regex.

import subprocess

import re

programs = input('Separe los programas con un espacio: ').split()

secure_pattern = '[\w\d]'

for program in programs:

 if not re.match(secure_pattern, program):
 print("Lo sentimos, no podemos comprobar ese programa")

 continue

 process = subprocess. run(
 ['cual', programa], capture_output=True, text=True)

 if process.returncode == 0:
 print(f'El programa "{program}" está instalado')

 print(f'La ubicación del binario es: {proceso.stdout}')
 else:
 print(f'Lo sentimos el {programa} no está instalado')

 print(process.stderr)

 print('\n')

En este caso, obtenemos los programas del usuario y utilizamos una expresión regex que certifica que la cadena del programa sólo incluye letras y dígitos. Comprobamos la existencia de cada programa con un bucle para a.

Nota: Compruebe este comprobador regex en línea.

Grep simple en Python

Su amigo Tom tiene una lista de patrones en un archivo de texto y otro archivo grande en el que quiere obtener el número de coincidencias de cada patrón. Se pasaría horas ejecutando el comando grep para cada patrón.

Afortunadamente, usted sabe cómo resolver este problema con Python, y le ayudará a realizar esta tarea en pocos segundos.

import subproceso

archivo_patrones = 'patrones.txt'
archivo_lectura = 'romeo-full.txt'

with open(archivo_patrones, 'r') as f:
 for patrón in f:
 patrón = patrón.strip()

 process = subprocess.run(
 ['grep', '-c', f'{patrón}', readfile], capture_output=True, text=True)

 if int(process.stdout) == 0:
 print(
 f'El patrón "{patrón}" no coincidió con ninguna línea de {readfile}')

 continue

 print(f'El patrón "{patrón}" coincidió {process.stdout.strip()} veces')

Echando un vistazo a este archivo, definimos dos variables que son los nombres de archivo con los que queremos trabajar. A continuación, abrimos el archivo que contiene todos los patrones e iteramos sobre ellos. A continuación, llamamos a un subproceso que ejecuta un comando grep con la bandera "-c" (significa contar) y determinamos la salida de la coincidencia con un condicional.

Si ejecuta este archivo (recuerde que puede descargar los archivos de texto de la repo de Github)

Configurar un virtualenv con subproceso

Una de las cosas más geniales que puede hacer con Python es la automatización de procesos. Este tipo de script puede ahorrarle horas de tiempo a la semana.

Por ejemplo, vamos a crear un script de configuración que cree un entorno virtual para nosotros e intente encontrar un archivo requisitos.txt en el directorio actual para instalar todas las dependencias.

¡import subprocess

from pathlib import Path


VENV_NAME = '.venv'
REQUIREMENTS = 'requirements.txt'

process1 = subprocess.run(['which', 'python3'], capture_output=True, text=True)

if process1.returncode != 0:
 raise OSError('Lo sentimos, python3 no está instalado')

python_bin = process1.stdout.strip()

print(f'Python encontrado en: {python_bin}')

process2 = subprocess.run('echo "$SHELL"', shell=True, capture_output=True, text=True)

shell_bin = process2.stdout.split('/')[-1]

create_venv = subprocess.run([python_bin, '-m', 'venv', VENV_NAME], check=True)

if create_venv.returncode == 0:
 print(f'Su venv {VENV_NAME} ha sido creado')

pip_bin = f'{VENV_NAME}/bin/pip3'

if Path(REQUIREMENTS).exists():
 print(f'Archivo de requisitos "{REQUIREMENTS}" encontrado')
 print('Instalando requisitos')
 subprocess.run([pip_bin, 'install', '-r', REQUIREMENTS])

 print('¡Proceso completado! Ahora active su entorno con "source .venv/bin/activate"')

else:
 print("No se han especificado requisitos...")

En este caso, estamos utilizando varios procesos y analizando los datos que necesitamos en nuestro script python. También estamos utilizando la biblioteca pathlib que nos permite averiguar si el archivo requisitos.txt existe.

Si ejecuta el archivo python obtendrá algunos mensajes útiles de lo que está ocurriendo con el sistema operativo.

❯ python setup.py 
Python encontrado en: /usr/bin/python3
Su venv .venv ha sido creado
Archivo de requisitos "requirements.txt" encontrado
Instalando requisitos
Recopilando asgiref==3.3.4 .......
¡Proceso completado! Ahora active su entorno con "source .venv/bin/activate"

Observe que obtenemos la salida del proceso de instalación porque no estamos redirigiendo la salida estándar a una variable.

Ejecutar otro Lenguaje de Programación

Podemos ejecutar otros lenguajes de programación con python y obtener la salida de esos archivos. Esto es posible porque los subprocesos interactúan directamente con el sistema operativo.

Por ejemplo, creemos un programa hola mundo en C y Java. Para ejecutar el siguiente archivo, necesitará instalar los compiladores de C y Java.

helloworld.cpp

#include <iostream>

int main(){
 std::cout << "Esto es un hola mundo en C " << std::endl;
 return 0;
}


holamundo.java

class HolaMundo{ 
 public static void main(String args[]){ 
 System.out.println("Esto es un hola mundo en Java"); 
 } 
} 


Sé que esto parece un montón de código en comparación con un simple one-liner de Python, pero esto es sólo para propósitos de prueba.

Vamos a crear un script en Python que ejecute todos los archivos C y Java de un directorio. Para ello primero queremos obtener una lista de archivos dependiendo de la extensión del archivo, ¡y glob ¡nos permite hacerlo fácilmente!

from glob import glob

# Obtiene los archivos con cada extensión
java_files = glob('*.java')

cpp_files = glob('*.cpp')

Después de esto, podemos empezar a utilizar subprocesos para ejecutar cada tipo de archivo.

for file in cpp_files:
 process = subprocess.run(f'g {archivo} -o out; ./out', shell=True, capture_output=True, text=True)
    
 output = process.stdout.strip() ' BTW this was runned by Python'

 print(output)

for file in java_files:
 without_ext = archivo.strip('.java')
 process = subprocess.run(f'java {file}; java {without_ext}',shell=True, capture_output=True, text=True)

 output = process.stdout.strip() ' Un subproceso Python ejecutó esto :)'
 print(output)

Un pequeño truco es utilizar la función de cadenas tira para modificar la salida y obtener sólo lo que necesitamos.

Nota: Tenga cuidado al ejecutar archivos Java o C grandes ya que estamos cargando su salida en memoria y eso podría producir una fuga de memoria.

Abrir programas externos

Podemos ejecutar otros programas simplemente llamando a su ubicación binaria a través de un subproceso.

Vamos a probarlo abriendo valiente, mi navegador web preferido.

import subproceso

subprocess.run('brave')

Esto abrirá una instancia del navegador, o simplemente otra pestaña si ya tiene el navegador en ejecución.

Navegador abierto

Como con cualquier otro programa que acepte banderas, podemos utilizarlas para producir el comportamiento deseado.

import subprocess

subprocess.run(['valiente', '--incognito'])
Bandera de incógnito

En resumen

Un subproceso es un proceso informático creado por otro proceso. Podemos comprobar los procesos que está ejecutando nuestro ordenador con herramientas como htop y el administrador de tareas.

Python tiene su propia biblioteca para trabajar con subprocesos. Actualmente, la función ejecute nos ofrece una interfaz sencilla para crear y gestionar subprocesos.

Podemos crear cualquier tipo de aplicación con ellos porque estamos interactuando directamente con el SO.

Por último, recuerde que la mejor forma de aprender es crear algo que le gustaría utilizar.

  • Daniel Díaz
    Autor
Gracias a nuestros patrocinadores
Más lecturas sobre desarrollo
Potencia tu negocio
Algunas de las herramientas y servicios que le ayudarán a hacer crecer su negocio.
  • Invicti utiliza el Proof-Based Scanning™ para verificar automáticamente las vulnerabilidades identificadas y generar resultados procesables en tan solo unas horas.
    Pruebe Invicti
  • Web scraping, proxy residencial, gestor de proxy, desbloqueador web, rastreador de motores de búsqueda, y todo lo que necesita para recopilar datos web.
    Pruebe Brightdata
  • Monday.com es un sistema operativo de trabajo todo en uno que te ayuda a gestionar proyectos, tareas, trabajo, ventas, CRM, operaciones, flujos de trabajo y mucho más.
    Prueba Monday
  • Intruder es un escáner de vulnerabilidades en línea que encuentra puntos débiles de ciberseguridad en su infraestructura, para evitar costosas violaciones de datos.
    Prueba Intruder