El control de procesos en Rust se divide en dos dominios: la gestión del ciclo de vida del proceso actual y la orquestación de procesos externos (subprocesos). Para finalizar el proceso actual, es vital distinguir entre un cierre limpio y uno abrupto. Retornar desde main es la forma más segura, ya que permite que se ejecuten los destructores (drop) de todas las variables en el stack. Sin embargo, std::process::exit(code) fuerza la terminación inmediata con un código específico, saltándose la fase de limpieza de memoria y destructores, lo que puede causar que recursos como archivos o bloqueos de mutex queden en un estado inconsistente. Por otro lado, std::process::abort() detiene el proceso de forma inmediata mediante una señal de aborto del sistema, sin realizar ningún tipo de limpieza.
Para la ejecución de comandos externos, Rust proporciona el módulo std::process::Command, que implementa el patrón builder. Este módulo permite configurar el entorno, el directorio de trabajo y la comunicación mediante tuberías (pipes). Existen tres métodos principales para ejecutar un comando: spawn() para lanzar un proceso en segundo plano (retornando un objeto Child), status() para esperar la finalización y obtener solo el código de salida, y output() para esperar la ejecución y capturar toda la salida estándar (stdout) y de error (stderr) en una estructura Output. Para enviar datos al stdin de un subproceso, se debe configurar stdin(Stdio::piped()) y utilizar el método take() sobre el handle de entrada del proceso hijo para obtener un escritor (Write).
use std::process::{Command, Stdio};
use std::io::Write;
use std::env;
fn main() {
// 1. Interacción con el entorno del proceso actual
let args: Vec<String> = env::args().collect();
println!("Argumentos recibidos: {:?}", args);
let home = env::var("HOME").unwrap_or_else(|_| "No definida".to_string());
println!("Variable HOME: {}", home);
// 2. Ejecución de un subproceso con captura de salida (output)
// Usamos 'echo' para demostrar la captura de stdout
let output = Command::new("echo")
.arg("Hola desde un subproceso")
.output()
.expect("Error al ejecutar el comando echo");
if output.status.success() {
let stdout_string = String::from_utf8_lossy(&output.stdout);
println!("Salida capturada: {}", stdout_string.trim());
}
// 3. Ejecución de un subproceso con comunicación via stdin (piped)
// Configuramos stdin como una tubería para poder escribir en él
let mut child = Command::new("cat")
.stdin(Stdio::piped())
.spawn()
.expect("Error al spawnear el proceso cat");
// Obtenemos el handle de escritura del stdin del hijo
// .take() mueve la propiedad del stdin al scope local
let mut stdin_handle = child.stdin.take().expect("Error al tomar stdin");
// Escribimos datos directamente en el proceso hijo
stdin_handle.write_all(b"Texto enviado vía stdin").expect("Error al escribir en stdin");
// El handle se cierra al salir del scope o al ser dropado, enviando EOF al hijo
drop(stdin_handle);
// Esperamos a que el proceso hijo termine para obtener su estado
let status = child.wait().expect("Error al esperar al proceso");
println!("El proceso 'cat' terminó con estado: {}", status);
}
Explicación del Código
env::args().collect(): Recupera los argumentos pasados por línea de comandos al proceso actual y los almacena en unVec<String>.env::var("HOME"): Intenta obtener el valor de la variable de entornoHOME. Se usaunwrap_or_elsepara manejar el caso de error si la variable no existe.Command::new("echo"): Inicializa un constructor para el comandoecho..arg("Hola desde un subproceso"): Añade un argumento al comando antes de ejecutarlo..output(): Ejecuta el comando, espera a que termine y captura su salida en una estructuraOutput. La variableoutputcontienestdout,stderrystatus..status.success(): Verifica si el proceso terminó con un código de salida exitoso (típicamente 0).String::from_utf8_lossy(&output.stdout): Convierte los bytes capturados enstdouta una cadena legible, manejando caracteres no UTF-8 de forma segura.Command::new("cat").stdin(Stdio::piped()): Configura el comandocatpara que su entrada estándar sea una tubería controlada por el proceso padre..spawn(): Ejecuta el proceso en segundo plano de forma asíncrona, permitiéndonos interactuar con él mientras corre, devolviendo unChild.child.stdin.take(): El métodotake()consume elOption<ChildStdin>que reside en el objetochild, moviendo la propiedad del stream de entrada alstdin_handle. Esto es necesario para poder escribir en él de forma exclusiva.stdin_handle.write_all(b"..."): Escribe una secuencia de bytes directamente en el canal de entrada del proceso hijo.child.wait(): Bloquea la ejecución del proceso padre hasta que el proceso hijo termine, devolviendo suExitStatus.
N° 123