Processo
stdlib::process é uma API no estilo builder para fazer spawn de comandos externos com controle total sobre argv: saída capturada, sobrescrita de variáveis de ambiente, diretório de trabalho, stdin via pipe, códigos de saída e pipes de leitura/escrita em tempo real. Internamente usa fork+execvp no POSIX e CreateProcess no Windows — não há shell intermediário, ou seja, não há risco de quoting incorreto nem superfície de ataque por injeção de shell.
Import
import stdlib::process::*;
Visão geral
O fluxo típico é: construir um Command, configurá-lo com métodos encadeados e depois chamar run() (síncrono, bloqueia até o processo terminar) ou spawn() (não bloqueante, retorna um Child).
let cmd: *Command = Command::new("git");
cmd.arg("rev-parse").arg("HEAD").capture_out();
let r: !ProcessResult = cmd.run();
if r.ok {
println!("HEAD =", r.val.stdout.trim());
}
cmd.free();
Superfície pública em resumo
| Item | Tipo | Resumo |
|---|---|---|
ProcessResult |
struct | Resultado de uma execução concluída (código de saída, saída capturada, informações de sinal). |
Command |
struct + builder | Configura e inicia um subprocesso. |
Child |
struct | Handle para um processo em execução após spawn. |
ChildStdin / ChildStdout / ChildStderr |
struct | Handles de pipe em tempo real expostos após spawn(). |
Command::new / arg / args / env / env_clear / cwd / stdin_str |
fn builder | Configura programa, argumentos, variáveis de ambiente, diretório e stdin. |
Command::capture_out / capture_err / capture |
fn builder | Solicita captura de saída (consumida por run()). |
Command::pipe_stdin / pipe_stdout / pipe_stderr |
fn builder | Solicita pipes em tempo real (consumidos por spawn()). |
Command::run |
fn | Executa de forma síncrona; retorna !ProcessResult. |
Command::spawn |
fn | Inicia sem aguardar; retorna !Child. |
Command::free |
fn | Libera o builder. |
Child::wait / kill |
fn | Bloqueia aguardando saída / envia sinal ao filho. |
ChildStdin::write / close |
fn | Alimenta e fecha o stdin do processo filho. |
ChildStdout::read / read_all / close |
fn | Lê o stdout do processo filho. |
ChildStderr::read / read_all / close |
fn | Lê o stderr do processo filho. |
process_kill |
fn | Envia sinal a um PID arbitrário. |
process_exists |
fn | Verifica se um PID está vivo. |
Tipos de resultado e configuração
ProcessResult
O resultado de uma execução concluída. Retornado por Command::run() e Child::wait().
pub struct ProcessResult {
pub exit_code: i32, // 0 = sucesso
pub stdout: string, // vazio se não capturado
pub stderr: string, // vazio se não capturado
pub signaled: bool, // true se o filho morreu por um sinal (POSIX)
pub signal: i32, // número do sinal; 0 se !signaled
}
| Campo | Tipo | Significado |
|---|---|---|
exit_code |
i32 |
Status de saída do processo; 0 indica sucesso. No POSIX, morte por sinal é reportada como 128 + sinal. |
stdout |
string |
stdout capturado, ou "" se nenhum flag de captura foi definido. |
stderr |
string |
stderr capturado, ou "" se nenhum flag de captura foi definido. |
signaled |
bool |
true se o processo filho foi encerrado por um sinal (somente POSIX). |
signal |
i32 |
Número do sinal que encerrou o processo; 0 quando signaled é false. |
Command
O builder. Construído com Command::new, configurado com os métodos encadeados abaixo e depois executado. Os campos são públicos, mas normalmente você só os acessa por meio dos métodos.
pub struct Command {
pub program: string,
pub args: *Vector<string>,
pub env_kv: *Vector<string>, // entradas no formato "KEY=VAL"
pub cwd_path: string,
pub stdin_payload: string,
pub flags: i32, // bit 0 captura stdout, bit 1 captura stderr,
// bit 2 env_clear, bit 3 pipe stdin,
// bit 4 pipe stdout, bit 5 pipe stderr
}
Child
Handle para um processo que foi iniciado via spawn e ainda está em execução. Os handles de pipe são não-nulos apenas quando o builder pipe_* correspondente foi usado.
pub struct Child {
pub pid: i32,
pub stdin: *ChildStdin, // null a menos que pipe_stdin() tenha sido chamado
pub stdout: *ChildStdout, // null a menos que pipe_stdout() tenha sido chamado
pub stderr: *ChildStderr, // null a menos que pipe_stderr() tenha sido chamado
}
Structs de handle de pipe
Cada uma envolve um único handle do sistema operacional (HANDLE no Windows, fd no POSIX).
pub struct ChildStdin { pub handle: i64 }
pub struct ChildStdout { pub handle: i64 }
pub struct ChildStderr { pub handle: i64 }
Construindo um comando
Todos os métodos do builder retornam *Command, o que permite encadeamento. Command::new aloca memória; libere-a com free() (idealmente via defer).
new / free
| Função | Assinatura | Descrição |
|---|---|---|
new |
fn new(program: string) -> *Command |
Cria um builder para program. Argumentos, variáveis de ambiente, diretório de trabalho e stdin herdam os do processo pai por padrão; a saída é encaminhada ao pai, a menos que um método capture_* seja chamado. |
free |
fn free(self: *Command) |
Libera o builder (seus vetores args e env_kv e a struct). Use com defer. |
let cmd: *Command = Command::new("echo");
defer cmd.free();
cmd.arg("hi").run();
Argumentos, variáveis de ambiente e diretório
| Método | Assinatura | Descrição |
|---|---|---|
arg |
fn arg(self: *Command, a: string) -> *Command |
Adiciona um argumento posicional. |
args |
fn args(self: *Command, vs: *Vector<string>) -> *Command |
Adiciona cada string de vs como argumento (copia as entradas; a posse de vs continua com você). |
env |
fn env(self: *Command, key: string, value: string) -> *Command |
Define uma variável de ambiente para o processo filho (sobrescreve o ambiente herdado). Armazenada internamente como "KEY=VAL". |
env_clear |
fn env_clear(self: *Command) -> *Command |
Descarta o ambiente do processo pai; somente as variáveis definidas via env(k,v) chegam ao filho. |
cwd |
fn cwd(self: *Command, path: string) -> *Command |
Define o diretório de trabalho do processo filho. |
stdin_str |
fn stdin_str(self: *Command, s: string) -> *Command |
Envia s ao stdin do filho (somente com run()); o pipe é fechado após a escrita, sinalizando EOF. |
let extras: *Vector<string> = Vector::new();
extras.push_all!("--verbose", "input.txt");
let cmd: *Command = Command::new("tool");
cmd.arg("build")
.args(extras)
.env_clear()
.env("PATH", "/usr/bin:/bin") // somente PATH chega ao filho agora
.cwd("/tmp/repo")
.stdin_str("data\n")
.capture();
let r: !ProcessResult = cmd.run();
if r.ok { println!("exit:", r.val.exit_code); }
cmd.free();
extras.free();
Captura de saída
Esses métodos definem flags de captura consumidos por run(). Sem nenhum deles, stdout e stderr do processo filho herdam os do pai, e ProcessResult.stdout/.stderr serão "".
| Método | Assinatura | Descrição |
|---|---|---|
capture_out |
fn capture_out(self: *Command) -> *Command |
Captura stdout em ProcessResult.stdout (define o bit 0 do flag). |
capture_err |
fn capture_err(self: *Command) -> *Command |
Captura stderr em ProcessResult.stderr (define o bit 1 do flag). |
capture |
fn capture(self: *Command) -> *Command |
Captura ambos (equivalente a capture_out().capture_err(); define os bits 0+1). |
let cmd: *Command = Command::new("wc");
cmd.arg("-l").stdin_str("a\nb\nc\n").capture_out().capture_err();
let r: !ProcessResult = cmd.run();
if r.ok {
if r.val.exit_code == 0 {
println!("lines:", r.val.stdout.trim()); // "3"
} else {
println!("failed:", r.val.stderr);
}
}
cmd.free();
Capturar apenas stderr é o idioma para "executar um compilador/linter e exibir seus diagnósticos em caso de falha":
let cmd: *Command = Command::new("gcc");
cmd.arg("missing.c").capture_err();
let r: !ProcessResult = cmd.run();
if r.ok {
if r.val.exit_code != 0 {
println!("compile failed:", r.val.stderr.trim());
} else {
println!("ok");
}
} else {
println!("could not launch gcc:", r.err);
}
cmd.free();
Solicitação de pipes em tempo real
Esses métodos definem flags respeitados apenas por spawn(). Após o spawn(), o handle Child.stdin / .stdout / .stderr correspondente será não-nulo.
| Método | Assinatura | Descrição |
|---|---|---|
pipe_stdin |
fn pipe_stdin(self: *Command) -> *Command |
Abre um pipe de stdin em tempo real (bit 3 do flag); use child.stdin.write(s) / .close(). |
pipe_stdout |
fn pipe_stdout(self: *Command) -> *Command |
Abre um pipe de stdout em tempo real (bit 4 do flag); leia com child.stdout.read(n) / .read_all(). |
pipe_stderr |
fn pipe_stderr(self: *Command) -> *Command |
Abre um pipe de stderr em tempo real (bit 5 do flag); leia com child.stderr.read(n) / .read_all(). |
Execução
run
pub fn run(self: *Command) -> !ProcessResult
Executa de forma síncrona e bloqueia até o processo filho terminar. Respeita capture_*, stdin_str, env, env_clear, cwd. Retorna Err apenas em caso de falha no spawn; uma saída com código diferente de zero ainda é ok(...).
let r: !ProcessResult = Command::new("echo").arg("hi").capture_out().run();
if r.ok {
println!("exit:", r.val.exit_code);
println!("out:", r.val.stdout.trim()); // "hi"
}
Como run() retorna !ProcessResult, você pode propagar falhas de spawn com ? e reservar Err para problemas genuínos de inicialização, mapeando uma saída com código não-zero para seu próprio erro:
fn capture_uname() -> !string {
let cmd: *Command = Command::new("uname");
defer cmd.free();
let r: ProcessResult = cmd.arg("-s").capture_out().run()?;
if r.exit_code != 0 { return err("uname failed"); }
return ok(r.stdout.trim());
}
spawn
pub fn spawn(self: *Command) -> !Child
Inicia o processo sem aguardar e retorna um Child. stdin/stdout/stderr herdam os do processo pai a menos que um método pipe_* tenha sido chamado. Os flags capture_* e stdin_str são ignorados neste caminho — use os pipes em tempo real em seu lugar.
let r: !Child = Command::new("sleep").arg("60").spawn();
if r.ok {
let ch: *Child = &r.val;
println!("pid:", ch.pid);
let k: ! = ch.kill(9); // SIGKILL no POSIX, TerminateProcess no Windows
if !k.ok { println!(k.err); }
let res: !ProcessResult = ch.wait();
if res.ok {
if res.val.signaled {
println!("killed by signal", res.val.signal);
} else {
println!("exited", res.val.exit_code);
}
}
}
Controlando um Child
wait / kill
| Método | Assinatura | Descrição |
|---|---|---|
wait |
fn wait(self: *Child) -> !ProcessResult |
Bloqueia até o processo filho terminar e coleta seu resultado. stdout/stderr são sempre vazios (spawn não captura). |
kill |
fn kill(self: *Child, sig: i32) -> ! |
Envia o sinal sig. POSIX: kill(pid, sig). Windows: ignora sig e chama TerminateProcess. Retorna Err em caso de falha. |
let r: !Child = Command::new("true").spawn();
if r.ok {
let res: !ProcessResult = r.val.wait();
if res.ok { println!("exited", res.val.exit_code); }
}
Pipes em streaming
Quando spawn() é chamado após pipe_stdin / pipe_stdout / pipe_stderr, o Child expõe handles de leitura/escrita em tempo real. Todos os métodos de leitura/escrita/fechamento retornam um Result e expõem erros do sistema operacional como err("...").
ChildStdin
| Método | Assinatura | Descrição |
|---|---|---|
write |
fn write(self: *ChildStdin, s: string) -> !i32 |
Escreve s no stdin do processo filho; retorna o número de bytes escritos. |
close |
fn close(self: *ChildStdin) -> ! |
Fecha o pipe (envia EOF). Idempotente — define o handle como -1. |
ChildStdout
| Método | Assinatura | Descrição |
|---|---|---|
read |
fn read(self: *ChildStdout, n: i32) -> !string |
Lê até n bytes; retorna "" em EOF. |
read_all |
fn read_all(self: *ChildStdout) -> !string |
Lê tudo até EOF. |
close |
fn close(self: *ChildStdout) -> ! |
Fecha o pipe de stdout. |
ChildStderr
| Método | Assinatura | Descrição |
|---|---|---|
read |
fn read(self: *ChildStderr, n: i32) -> !string |
Lê até n bytes; retorna "" em EOF. |
read_all |
fn read_all(self: *ChildStderr) -> !string |
Lê tudo que foi escrito no stderr até EOF. |
close |
fn close(self: *ChildStderr) -> ! |
Fecha o pipe de stderr. |
Um ciclo completo de ida e volta pelo cat — escreve a entrada, fecha stdin para EOF, drena stdout e então chama wait():
let cmd: *Command = Command::new("cat");
cmd.pipe_stdin().pipe_stdout();
let r: !Child = cmd.spawn();
if r.ok {
let ch: *Child = &r.val;
if ch.stdin != null {
let w: !i32 = ch.stdin.write("hello\n");
if w.ok { println!("wrote", w.val); }
let c: ! = ch.stdin.close(); // EOF
if !c.ok { println!(c.err); }
}
if ch.stdout != null {
let all: !string = ch.stdout.read_all();
if all.ok { print!(all.val); }
let oc: ! = ch.stdout.close();
if !oc.ok { println!(oc.err); }
}
let done: !ProcessResult = ch.wait();
if done.ok { println!("exit", done.val.exit_code); }
}
cmd.free();
Para transmitir a saída em tempo real sem acumular tudo em buffer, faça um loop com read(n) limitado e pare quando retornar uma string vazia (EOF):
let r: !Child = Command::new("ls").arg("-la").pipe_stdout().spawn();
if r.ok {
let ch: *Child = &r.val;
if ch.stdout != null {
while true {
let chunk: !string = ch.stdout.read(4096);
if !chunk.ok { println!(chunk.err); break; }
if chunk.val.len() == 0 { break; } // EOF
print!(chunk.val);
}
let c: ! = ch.stdout.close();
if !c.ok { println!(c.err); }
}
let done: !ProcessResult = ch.wait();
if done.ok { println!("exit", done.val.exit_code); }
}
Funções auxiliares no nível do módulo
Atuam diretamente sobre um PID, sem envolver Command ou Child.
| Função | Assinatura | Descrição |
|---|---|---|
process_kill |
fn process_kill(pid: i32, sig: i32) -> ! |
Envia o sinal sig ao pid. POSIX: kill(pid, sig). Windows: TerminateProcess (sig é ignorado). Retorna Err em caso de falha. |
process_exists |
fn process_exists(pid: i32) -> bool |
true se um processo ativo possui este PID. POSIX: kill(pid, 0). Windows: OpenProcess + GetExitCodeProcess(STILL_ACTIVE). |
if process_exists(1234) {
let r: ! = process_kill(1234, 15); // SIGTERM
if !r.ok { println!(r.err); }
} else {
println!("pid 1234 not running");
}
Advertências e notas de plataforma
- Sem shell.
Command::new("ls -la")procura por um programa literalmente chamado
ls -la. Passe o programa e cada argumento separadamente: Command::new("ls").arg("-la"). Para usar recursos do shell, invoque-o explicitamente:
let cmd: *Command = Command::new("sh");
cmd.arg("-c").arg("ls -la | wc -l").capture_out();
let r: !ProcessResult = cmd.run();
if r.ok && r.val.exit_code == 0 {
println!("entries:", r.val.stdout.trim());
}
cmd.free();
```
- **Streaming e `run()` não se misturam.** `run()` ignora `pipe_*`; `spawn()` ignora
`capture_*` e `stdin_str`. Escolha um modelo por comando.
- **Sinais são moldados pelo POSIX.** `signaled` / `signal` só têm significado no POSIX;
no Windows, `kill` sempre força o encerramento independentemente do valor de `sig`.
- **Código de saída `127`** retornado por um `run()` que deu `ok` geralmente significa
que o programa não foi encontrado ou que o `chdir` para o `cwd` falhou no filho
(uma falha de `execvp` / `chdir` dentro do processo filho faz com que ele chame
`_exit(127)`).
- **Libere o builder.** `Command::new` aloca com `malloc`; sempre chame `free()`
(use `defer cmd.free();` logo após a construção) para liberar a struct e seus vetores
internos.
## Veja também
- [`stdlib::os`](09-os.md) — primitivas de nível mais baixo do sistema operacional (`os_shell` e afins).
- [`stdlib::env`](10-env.md) — leitura e inspeção das variáveis de ambiente do processo pai.
- [`fs-io`](08-fs-io.md) — `eprintln` e auxiliares de stream para registrar a saída capturada.