std.process em Zig — Referência e Exemplos

std.process — Gerenciamento de Processos

O módulo std.process fornece funcionalidades para gerenciar processos no Zig. Isso inclui a execução de processos filhos, acesso a variáveis de ambiente, leitura de argumentos de linha de comando e controle do ciclo de vida de processos. Este módulo é essencial para construir ferramentas CLI, scripts de automação e qualquer aplicação que precise interagir com outros programas do sistema.

Visão Geral

const std = @import("std");
const process = std.process;

O std.process organiza suas funcionalidades em torno de três áreas principais: execução de processos filhos via Child, acesso ao ambiente do processo atual e manipulação de argumentos.

Funções Principais

Argumentos de Linha de Comando

// Obtém os argumentos de linha de comando
pub fn std.process.argsAlloc(allocator: Allocator) ![][:0]u8

// Libera os argumentos alocados
pub fn std.process.argsFree(allocator: Allocator, args: [][:0]u8) void

// Iterador sobre argumentos (sem alocação)
pub fn std.process.args() ArgIterator

Variáveis de Ambiente

// Obtém mapa completo de variáveis de ambiente
pub fn std.process.getEnvMap(allocator: Allocator) !EnvMap

// Variável individual (via std.posix)
pub fn std.posix.getenv(name: []const u8) ?[]const u8

Processos Filhos (Child)

// Cria e configura um processo filho
pub const Child = struct {
    pub fn init(argv: []const []const u8, allocator: Allocator) Child
    pub fn spawn(self: *Child) !void
    pub fn wait(self: *Child) !Term
    pub fn kill(self: *Child) !void
};

Exemplo 1: Leitura de Argumentos

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    // Método 1: Iterador (sem alocação de heap)
    var args = std.process.args();

    try stdout.writeAll("=== Argumentos (Iterador) ===\n");
    var i: usize = 0;
    while (args.next()) |arg| {
        try stdout.print("  argv[{d}] = {s}\n", .{ i, arg });
        i += 1;
    }

    // Método 2: Todos de uma vez (aloca no heap)
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const todos_args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, todos_args);

    try stdout.print("\nTotal de argumentos: {d}\n", .{todos_args.len});

    // Processa flags simples
    for (todos_args[1..]) |arg| {
        if (std.mem.eql(u8, arg, "--verbose") or std.mem.eql(u8, arg, "-v")) {
            try stdout.writeAll("Modo verbose ativado!\n");
        } else if (std.mem.startsWith(u8, arg, "--output=")) {
            const valor = arg["--output=".len..];
            try stdout.print("Arquivo de saída: {s}\n", .{valor});
        } else if (!std.mem.startsWith(u8, arg, "-")) {
            try stdout.print("Argumento posicional: {s}\n", .{arg});
        }
    }
}

Exemplo 2: Variáveis de Ambiente

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const stdout = std.io.getStdOut().writer();

    // Acesso individual via std.posix
    try stdout.writeAll("=== Variáveis Importantes ===\n");
    const vars_interesse = [_][]const u8{ "HOME", "USER", "PATH", "SHELL", "LANG", "EDITOR" };

    for (vars_interesse) |nome| {
        if (std.posix.getenv(nome)) |valor| {
            const preview = if (valor.len > 50) valor[0..50] else valor;
            const sufixo: []const u8 = if (valor.len > 50) "..." else "";
            try stdout.print("  {s:<8} = {s}{s}\n", .{ nome, preview, sufixo });
        } else {
            try stdout.print("  {s:<8} = (indefinida)\n", .{nome});
        }
    }

    // Mapa completo de variáveis de ambiente
    var env_map = try std.process.getEnvMap(allocator);
    defer env_map.deinit();

    try stdout.print("\nTotal de variáveis de ambiente: {d}\n", .{env_map.count()});

    // Filtra variáveis que começam com prefixo específico
    try stdout.writeAll("\n=== Variáveis XDG ===\n");
    var iter = env_map.iterator();
    var contagem_xdg: u32 = 0;
    while (iter.next()) |entry| {
        if (std.mem.startsWith(u8, entry.key_ptr.*, "XDG_")) {
            try stdout.print("  {s} = {s}\n", .{ entry.key_ptr.*, entry.value_ptr.* });
            contagem_xdg += 1;
        }
    }
    if (contagem_xdg == 0) {
        try stdout.writeAll("  (nenhuma variável XDG encontrada)\n");
    }
}

Exemplo 3: Executando Processos Filhos

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const stdout = std.io.getStdOut().writer();

    // Executa comando e captura saída
    try stdout.writeAll("=== Executando 'uname -a' ===\n");
    const resultado = try std.process.Child.run(.{
        .allocator = allocator,
        .argv = &.{ "uname", "-a" },
    });
    defer allocator.free(resultado.stdout);
    defer allocator.free(resultado.stderr);

    try stdout.print("Saída: {s}\n", .{resultado.stdout});
    try stdout.print("Status: {}\n", .{resultado.term});

    // Executa múltiplos comandos e compara
    try stdout.writeAll("\n=== Informações do Sistema ===\n");

    const comandos = [_]struct { nome: []const u8, args: []const []const u8 }{
        .{ .nome = "Hostname", .args = &.{"hostname"} },
        .{ .nome = "Whoami", .args = &.{"whoami"} },
        .{ .nome = "Data", .args = &.{ "date", "+%Y-%m-%d %H:%M:%S" } },
        .{ .nome = "Uptime", .args = &.{"uptime"} },
    };

    for (comandos) |cmd| {
        const res = std.process.Child.run(.{
            .allocator = allocator,
            .argv = cmd.args,
        }) catch |err| {
            try stdout.print("  {s}: erro - {}\n", .{ cmd.nome, err });
            continue;
        };
        defer allocator.free(res.stdout);
        defer allocator.free(res.stderr);

        // Remove newline final
        const saida = std.mem.trimRight(u8, res.stdout, "\n");
        try stdout.print("  {s}: {s}\n", .{ cmd.nome, saida });
    }
}

Exemplo 4: Construtor de Processos com Pipes

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    const stdout = std.io.getStdOut().writer();

    // Processo com controle manual de spawn/wait
    var child = std.process.Child.init(
        .{ .argv = &.{ "echo", "Olá do processo filho!" } },
        allocator,
    );

    child.stdout_behavior = .pipe;
    try child.spawn();

    // Lê stdout do processo filho
    const child_stdout = child.stdout.?.reader();
    const output = try child_stdout.readAllAlloc(allocator, 4096);
    defer allocator.free(output);

    const term = try child.wait();
    try stdout.print("Saída do filho: {s}", .{output});
    try stdout.print("Código de saída: {}\n", .{term});

    // Encadeando processos (pipeline)
    try stdout.writeAll("\n=== Pipeline: ls | wc -l ===\n");
    const pipeline_result = try std.process.Child.run(.{
        .allocator = allocator,
        .argv = &.{ "sh", "-c", "ls -la /tmp | head -5" },
    });
    defer allocator.free(pipeline_result.stdout);
    defer allocator.free(pipeline_result.stderr);

    try stdout.print("{s}\n", .{pipeline_result.stdout});
}

Tratamento de Erros

O std.process usa o sistema de erros do Zig extensivamente. Os erros mais comuns incluem:

ErroDescrição
error.FileNotFoundComando não encontrado no PATH
error.AccessDeniedSem permissão para executar
error.OutOfMemoryMemória insuficiente
error.UnexpectedErro de sistema inesperado
const result = std.process.Child.run(.{
    .allocator = allocator,
    .argv = &.{"comando_inexistente"},
}) catch |err| switch (err) {
    error.FileNotFound => {
        std.debug.print("Comando não encontrado!\n", .{});
        return;
    },
    error.AccessDenied => {
        std.debug.print("Sem permissão!\n", .{});
        return;
    },
    else => return err,
};

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.