En Zig, la creación de procesos hijos se realiza a través de la estructura std.ChildProcess. Su método init permite configurar todos los parámetros necesarios: comando, argumentos, variables de entorno y directorio de trabajo. Una vez inicializado, el proceso se puede lanzar con spawn, esperar a que termine con wait y, si se desea, capturar su salida estándar.
Pasos básicos para usar std.ChildProcess.init
- Importar el módulo estándar y la estructura
ChildProcess. - Obtener un allocador, generalmente
std.heap.page_allocator. - Llamar a
ChildProcess.initpasando el comando, los argumentos y opcionalmenteenvycwd. - Ejecutar el proceso con
spawn. - Esperar su terminación con
waity leer el código de salida. - Liberar los recursos con
destroycuando ya no se necesiten.
Ejemplo sencillo: listar archivos con ls
const std = @import("std"); // 1
const ChildProcess = std.ChildProcess; // 2
const Allocator = std.mem.Allocator; // 3
pub fn main() !void { // 4
const gpa = std.heap.page_allocator; // 5
// Creamos un ChildProcess con el comando "ls" y los argumentos "-l" y "-a"
var child = ChildProcess.init( // 6
gpa, // 7 Allocator
"ls", // 8 comando a ejecutar
&[_][]const u8{ "-l", "-a" }, // 9 argumentos del comando
null, // 10 variables de entorno (none)
null // 11 directorio de trabajo (current)
); // 12
errdefer child.destroy(); // 13 asegurar limpieza en caso de error
try child.spawn(); // 14 lanzar el proceso
const exit = try child.wait(); // 15 esperar a que termine
std.debug.print( // 16 mostrar el código de salida
"Child exited with status {}\n", .{exit.code}
); // 17
} // 18
En el ejemplo anterior:
ChildProcess.initconfigura el proceso con el comando y los argumentos.spawnlo ejecuta; si falla se genera un error que se propaga contry.- Tras
wait,exit.codecontiene el código de salida del hijo. - El bloque
errdefergarantiza quedestroyse ejecute sispawnowaitfallan.
Capturar la salida estándar
Si quieres leer la salida que genera el proceso, configura los stdout_behavior y stderr_behavior antes de spawn:
const std = @import("std");
const ChildProcess = std.ChildProcess;
pub fn main() !void {
const gpa = std.heap.page_allocator;
var child = ChildProcess.init(
gpa,
"ping",
&[_][]const u8{ "-c", "2", "example.com" }, // ping dos veces
null,
null
);
// Capturamos stdout y stderr
child.stdout_behavior = .capture;
child.stderr_behavior = .capture;
try child.spawn();
// Esperamos la terminación
const exit = try child.wait();
// Convertimos la salida capturada en una cadena de bytes
const stdout = try child.stdout.?.toOwnedSlice(gpa);
defer gpa.free(stdout);
const stderr = try child.stderr.?.toOwnedSlice(gpa);
defer gpa.free(stderr);
std.debug.print("Código de salida: {}\n", .{exit.code});
std.debug.print("stdout: {s}\n", .{stdout});
std.debug.print("stderr: {s}\n", .{stderr});
}
Detalles clave:
.captureindica que Zig debe asignar memoria y almacenar la salida.- Después de
waitse accede a los buffers conchild.stdout.?.ychild.stderr.?.. - Los buffers son de tipo
[]const u8; se convierten a slices manejados por el allocador para liberar correctamente.
Definir variables de entorno y directorio de trabajo
Para pasar variables de entorno personalizadas o cambiar el directorio de trabajo, simplemente provee los argumentos correspondientes a init:
const std = @import("std");
const ChildProcess = std.ChildProcess;
pub fn main() !void {
const gpa = std.heap.page_allocator;
// Variables de entorno: KEY=VALUE
const env_vars = &[_][]const u8{ "MYVAR=123", "ANOTHER=hello" };
// Directorio de trabajo (por ejemplo, /tmp)
const cwd = "/tmp";
var child = ChildProcess.init(
gpa,
"printenv",
&[_][]const u8{}, // sin argumentos
env_vars, // variables de entorno
cwd // directorio actual
);
child.stdout_behavior = .capture; // capturar salida
