Executando comandos em Rust: como capturar a saída em 2025

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Do Python ao Rust: a evolução da execução de comandos

\n\n

Se você já se perguntou "How do I invoke a system command and capture its output?" no Stack Overflow, sabe que esta é uma das perguntas mais populares e vitais para qualquer desenvolvedor. No mundo Python, a resposta é simples — subprocess.check_output(). Mas o que fazer se você migrou para o Rust e quer não apenas executar um comando, mas fazê-lo de forma elegante, segura e com máxima performance?

\n\n

Rust não é apenas "mais uma linguagem". É um sistema onde cada byte está sob controle, e erros na execução de comandos não levam à falha de toda a aplicação. Em 2025, quando segurança e velocidade se tornaram o padrão de facto, saber trabalhar corretamente com subprocessos em Rust é uma habilidade indispensável. Vamos analisar as melhores práticas que transformarão a execução de comandos do sistema de uma rotina em uma arte.

\n\n

std::process::Command: seu canivete suíço

\n\n

O coração da execução de comandos em Rust é o módulo std::process::Command. Ele fornece tudo o que é necessário para executar programas externos, capturar sua saída e tratar erros. Ao contrário do Python, onde subprocess esconde muitos detalhes, o Rust lhe dá controle total.

\n\n

Exemplo básico: captura de stdout

\n\n

Vamos começar com o clássico — executar o comando echo "Hello, Rust!" e capturar sua saída:

\n\n
use std::process::Command;\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let output = Command::new("echo")\n        .arg("Hello, Rust!")\n        .output()?;\n\n    let stdout = String::from_utf8(output.stdout)?;\n    println!("Saída do comando: {}", stdout);\n\n    Ok(())\n}\n
\n\n

O que é importante aqui? Usamos .output(), que retorna uma estrutura Output. Ela contém três campos: stdout, stderr e status. Observe o ? — este é o operador de propagação de erros, que nos livra de blocos match volumosos. O Rust nos obriga a tratar explicitamente possíveis falhas: o comando pode não existir, não ter permissões de acesso ou simplesmente terminar com erro.

\n\n

Tratamento de stderr e código de retorno

\n\n

Em cenários reais, você precisa não apenas do stdout, mas também do stderr. Imagine que você executa ls /nonexistent — um comando que certamente gerará um erro:

\n\n
use std::process::Command;\n\nfn main() {\n    let output = Command::new("ls")\n        .arg("/nonexistent")\n        .output()\n        .expect("Falha ao executar o comando");\n\n    if output.status.success() {\n        let stdout = String::from_utf8_lossy(&output.stdout);\n        println!("Sucesso: {}", stdout);\n    } else {\n        let stderr = String::from_utf8_lossy(&output.stderr);\n        eprintln!("Erro (código {}): {}", output.status.code().unwrap_or(-1), stderr);\n    }\n}\n
\n\n

Aqui usamos String::from_utf8_lossy() — uma forma segura de converter bytes em string, que substitui caracteres UTF-8 inválidos por �. Isso é crítico se a saída do comando contiver dados binários ou usar uma codificação imprevisível.

\n\n

Técnicas avançadas: assincronia e pipes

\n\n

Em 2025, a execução síncrona de comandos é coisa do passado. Aplicações modernas exigem assincronia, especialmente se você trabalha com servidores web ou processa vários comandos simultaneamente. O Rust, com seu ecossistema Tokio, oferece uma solução elegante.

\n\n

Captura assíncrona de saída com Tokio

\n\n

Adicione a dependência no Cargo.toml:

\n\n
[dependencies]\ntokio = { version = "1", features = ["full"] }\n
\n\n

Agora execute o comando de forma assíncrona:

\n\n
use tokio::process::Command;\n\n#[tokio::main]\nasync fn main() -> Result<(), Box<dyn std::error::Error>> {\n    let output = Command::new("sleep")\n        .arg("2")\n        .output()\n                

Blogs

Book Recommendations