std::process::Command
Command
1.基础用法
执行命令,返回结果
use std::process::Command;
fn main() {
let mut cmd = Command::new("rustc");
cmd.arg("--version");
let output = cmd.output().expect("failed to execute rustc");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "rustc 1.93.0 (254b59607 2026-01-19)\n",
stderr: "",
}
链式调用
use std::process::Command;
fn main() {
// 链式调用
let output = Command::new("rustc")
.arg("--version")
.output()
.expect("failed to execute rustc");
println!("{:#?}", output);
}
2.传参
arg和args可以混用
2.1 arg单个参数
接收单个参数(字符串 / 路径等可转为
OsStr的类型)⚠️避免将多个参数拼为一个字符串传递(比如
arg("-l /tmp"))
use std::process::Command;
fn main() {
let output = Command::new("ls")
.arg("-l")
.arg("/Code/rust_code/cmd")
.output()
.expect("failed to execute rustc");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "total 16\n-rw-r--r-- 1 lxd670 staff 147 Mar 10 20:36 Cargo.lock\n-rw-r--r-- 1 lxd670 staff 74 Mar 10 20:33 Cargo.toml\ndrwxr-xr-x 3 lxd670 staff 96 Mar 10 21:15 src\ndrwxr-xr-x@ 5 lxd670 staff 160 Mar 10 20:36 target\n",
stderr: "",
}
2.2 args多个参数
接收迭代器类型(数组、Vec、切片等)
use std::process::Command;
fn main() {
let output = Command::new("ls")
.args(["-l", "/Code/rust_code/cmd"])
.output()
.expect("failed to execute rustc");
println!("{:?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "total 16\n-rw-r--r-- 1 lxd670 staff 147 Mar 10 20:36 Cargo.lock\n-rw-r--r-- 1 lxd670 staff 74 Mar 10 20:33 Cargo.toml\ndrwxr-xr-x 3 lxd670 staff 96 Mar 10 21:15 src\ndrwxr-xr-x@ 5 lxd670 staff 160 Mar 10 20:36 target\n",
stderr: "",
}
2.3 arg和args混用
use std::process::Command;
fn main() {
let output = Command::new("ls")
.arg("-l")
.args(["-a", "/my_space/Code/rust_code/cmd"])
.output()
.expect("failed to execute rustc");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "total 24\ndrwxr-xr-x 8 lxd670 staff 256 Mar 10 20:36 .\ndrwxr-xr-x 21 lxd670 staff 672 Mar 10 20:33 ..\ndrwxr-xr-x 9 lxd670 staff 288 Mar 10 21:12 .git\n-rw-r--r-- 1 lxd670 staff 8 Mar 10 20:33 .gitignore\n-rw-r--r-- 1 lxd670 staff 147 Mar 10 20:36 Cargo.lock\n-rw-r--r-- 1 lxd670 staff 74 Mar 10 20:33 Cargo.toml\ndrwxr-xr-x 3 lxd670 staff 96 Mar 10 21:17 src\ndrwxr-xr-x@ 5 lxd670 staff 160 Mar 10 20:36 target\n",
stderr: "",
}
3.设置目录
3.1 current_dir
等价于
cd <dir> && <cmd>执行
use std::process::Command;
fn main() {
let output = Command::new("ls")
.current_dir("/Code/rust_code/cmd")
.arg("-l")
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "total 16\n-rw-r--r-- 1 lxd670 staff 147 Mar 10 20:36 Cargo.lock\n-rw-r--r-- 1 lxd670 staff 74 Mar 10 20:33 Cargo.toml\ndrwxr-xr-x 3 lxd670 staff 96 Mar 10 21:28 src\ndrwxr-xr-x@ 5 lxd670 staff 160 Mar 10 20:36 target\n",
stderr: "",
}
4.环境变量
4.1 env
添加单个环境变量
.arg("echo").arg("$TESTA")写法Rust 会把字符串$FOO"作为一个普通的文本参数传递给echo有值就覆盖,没值就新建
use std::process::Command;
fn main() {
let output = Command::new("sh")
.arg("-c")
.arg("echo $TEST")
.env("TEST", "test")
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "test\n",
stderr: "",
}
4.1.1 传递给脚本
# a.sh
echo $AAA
use std::process::Command;
fn main() {
let output = Command::new("./a.sh")
.env("AAA", "ccc")
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "ccc\n",
stderr: "",
}
4.2 envs
添加多个环境变量
⚠️
("AAA", 1)不能传入数字类型,因为没有现实AsRef<OsStr>可以传入
HashMap或向量元组
# a.sh
echo $AAA
echo $BBB
use std::process::Command;
fn main() {
let env_args = [
("AAA", "1"),
("BBB", "2"),
];
let output = Command::new("./a.sh")
.envs(env_args)
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "1\n2\n",
stderr: "",
}
4.3 env_clear
清空所有环境变量
默认情况下,子进程会继承父进程的所有环境变量。
# a.sh
echo $AAA
echo $BBB
use std::env;
use std::process::Command;
fn main() {
unsafe {
env::set_var("AAA", "mainAAA");
env::set_var("BBB", "mainBBB");
}
let output = Command::new("./a.sh")
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "mainAAA\nmainBBB\n",
stderr: "",
}
清空所有env案列
use std::env;
use std::process::Command;
fn main() {
unsafe {
env::set_var("AAA", "mainAAA");
env::set_var("BBB", "mainBBB");
}
let output = Command::new("./a.sh")
.env_clear()
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "\n\n",
stderr: "",
}
4.4 env_remove
移除指定环境变量
# a.sh
echo $AAA
echo $BBB
use std::env;
use std::process::Command;
fn main() {
unsafe {
env::set_var("AAA", "mainAAA");
env::set_var("BBB", "mainBBB");
}
let output = Command::new("./a.sh")
.env_remove("AAA")
.output()
.expect("failed to execute process");
println!("{:#?}", output);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "\nmainBBB\n",
stderr: "",
}
5.获取cmd内容
获取执行路径:
get_current_dir() -> Option<&Path>
获取命令:get_program() -> &OsStr
获取环境变量:get_envs() -> CommandEnvs
获取执行参数:get_args() -> CommandArgs
use std::env;
use std::process::Command;
fn main() {
let mut c = Command::new("ls");
unsafe {
env::set_var("RUSTA", "1");
}
c.arg("-l");
c.env("AAA", "123");
c.current_dir("/test_rust/test_trait");
println!("{:#?}", c);
match c.get_current_dir() {
Some(path) => {
// path 是 &Path,可以转换为字符串
println!("Working directory: {}", path.display());
// 或者转换为 OsString/String
let path_str = path.to_string_lossy();
println!("Path as string: {}", path_str);
}
None => {
println!("Using current working directory (inherited)");
}
}
println!("program: {}", c.get_program().to_string_lossy());
println!("envs: {:?}", c.get_envs().collect::<Vec<_>>());
println!("args: {:?}", c.get_args().collect::<Vec<_>>());
}
Command {
program: "ls",
args: [
"ls",
"-l",
],
env: CommandEnv {
clear: false,
vars: {
"AAA": Some(
"123",
),
},
},
cwd: Some(
"/test_rust/test_trait",
),
}
Working directory: /test_rust/test_trait
Path as string: /test_rust/test_trait
program: ls
envs: [("AAA", Some("123"))]
args: ["-l"]
6.处理标准输入输出
| 特性 | output() |
status() |
spawn() |
|---|---|---|---|
| 阻塞行为 | 阻塞直到完成 | 阻塞直到完成 | 立即返回 (非阻塞) |
| Stdout/Stderr | 捕获 (存入内存) | 继承 (通常打印到终端) | 可配置 (默认继承,可重定向为管道) |
| 返回值 | Output (含状态 + 数据) |
ExitStatus (仅状态) |
Child (进程句柄) |
| 内存占用 | 高 (取决于输出大小) | 低 | 低 (除非手动缓冲) |
| 典型用途 | 获取命令结果字符串 | 运行安装脚本/构建工具 | 长期服务/交互式进程/并发 |
6.1 output
捕获所有输出,等待进程结束,返回输出内容
子进程打印到屏幕上的所有内容(正常输出和错误信息),不会直接显示在你当前的终端里,而是被 Rust 程序“拦截”并保存到了内存中。
.stdout和.stderr字段中读取字节数据(Vec<u8>)需要转换为utf8字符串
- 输入输出配置(固定不允许修改)
- stdin: Null(子进程无法读取输入)
- stdout: Piped(强制捕获到内存)
- stderr: Piped(强制捕获到内存)
6.1.1 基础样例
use std::process::Command;
fn main() {
let output = Command::new("ls")
.output()
.expect("Failed to execute command");
println!("状态: {}", output.status);
println!("_stdout_: {:?}", String::from_utf8_lossy(&output.stdout));
println!("_stderr_: {:?}", String::from_utf8_lossy(&output.stderr));
}
状态: exit status: 0
_stdout_: "Cargo.lock\nCargo.toml\nsrc\ntarget\n"
_stderr_: ""
6.1.1 错误处理
6.1.1.1 expect报错
找不到命令
如果命令执行失败,expect会抛出一个异常,然后程序会panic
use std::process::Command;
fn main() {
// 使用expect之后,会抛出异常,然后panic程序
let output = Command::new("ls1")
.output()
.expect("Failed to execute command");
println!("状态: {}", output.status.success());
println!("状态: {}", output.status);
println!("_stdout_: {:?}", String::from_utf8_lossy(&output.stdout));
println!("_stderr_: {:?}", String::from_utf8_lossy(&output.stderr));
}
6.1.1.2 match捕获
命令执行成功
Err表示连l1s命令都找不到,所以不会出现status、stdout、stderr
use std::process::Command;
fn main() {
let output = Command::new("l1s")
.output();
match output {
Ok(output) => {
println!("状态: {}", output.status.success());
println!("状态: {}", output.status);
println!("_stdout_: {:?}", String::from_utf8_lossy(&output.stdout));
println!("_stderr_: {:?}", String::from_utf8_lossy(&output.stderr));
},
Err(e) => {
println!("{}", e);
}
}
}
6.1.1.3 捕获脚本错误
命令执行成功
Output 结构体中的 status、stdout 和 stderr 捕获的是子进程在运行期间产生的所有结果
# 默认echo "你好" >&1 输出到标准输出
echo "你好"
# 错误输出到标准错误
echo "错误" >&2
exit 1
use std::process::Command;
fn main() {
let output = Command::new("./a.sh")
.current_dir("/test_rust/test_trait/src")
.output();
match output {
Ok(output) => {
println!("状态: {}", output.status.success());
println!("状态: {}", output.status);
println!("_stdout_: {:?}", String::from_utf8_lossy(&output.stdout));
println!("_stderr_: {:?}", String::from_utf8_lossy(&output.stderr));
},
Err(e) => {
println!("{}", e);
}
}
}
状态: false
状态: exit status: 1
_stdout_: "你好\n"
_stderr_: "错误\n"
6.2 status
不捕获输出(通常继承自父进程),等待进程结束,只返回退出状态码。
输出内容直接显示在终端
- 输入输出配置(固定不允许修改)
- stdin: Inherit(通常继承自父进程)
- stdout: Inherit(直接打印到终端)
- stderr: Inherit(直接打印到终端)
use std::process::Command;
fn main() {
let s = Command::new("ls")
.current_dir("/test_rust/test_trait/src")
.status()
.expect("Failed to execute command");
// s是Result<ExitStatus>
println!("状态: {:?}", s);
println!("状态: {}", s.code().unwrap());
}
main.rs
状态: ExitStatus(unix_wait_status(0))
状态: 0
6.2.1 错误处理
6.2.1.1 expect
程序直接panic了
use std::process::Command;
fn main() {
let s = Command::new("ls1")
.status()
.expect("Failed to execute command");
// s是Result<ExitStatus>
println!("状态: {:?}", s);
println!("状态: {}", s.code().unwrap());
}
Failed to execute command: Os { code: 2, kind: NotFound, message: "No such file or directory" }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
6.2.1.2 捕获脚本错误
use std::process::Command;
fn main() {
let s = Command::new("./a.sh")
.current_dir("/test_rust/test_trait/src")
.status()
.expect("Failed to execute command");
// s是Result<ExitStatus>
println!("状态: {:?}", s);
println!("状态: {}", s.code().unwrap());
}
你好
错误
状态: ExitStatus(unix_wait_status(256))
状态: 1
6.3 spawn
立即返回,不等待进程结束,返回一个句柄供
Child你手动控制(异步执行或流式处理)
- 输入输出配置(允许修改)
- stdin: Inherit(通常继承自父进程)
- stdout: Inherit(直接打印到终端)
- stderr: Inherit(直接打印到终端)
6.3.1 child.id()
获取子进程的PID
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to start");
println!("id: {}", child.id());
child.wait().expect("Failed to wait on child");
}
id: 2762
6.3.2 child.wait()
阻塞当前线程:调用后,当前代码会停在这里不动,直到子进程彻底退出
回收资源:子进程结束后,操作系统回收其 PID 和资源,避免僵尸进程。
返回退出状态:返回
Result<ExitStatus>
use std::process::{Command, Stdio};
use chrono::prelude::Local;
fn main() {
let mut child = Command::new("sleep") // 用一个长运行的命令
.arg("10")
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start");
println!("satrt: {}", Local::now());
let res = child.wait().expect("Failed to wait on child");
println!("res: {:?}", res);
println!("end: {}", Local::now())
}
satrt: 2026-03-16 22:37:03.378065 +08:00
res: ExitStatus(unix_wait_status(0))
end: 2026-03-16 22:37:13.383562 +08:00
6.3.3 child.try_wait()
不阻塞:调用后立即返回,不管子进程有没有结束
检查子进程状态,返回
Option<ExitStatus>只有当它返回
Some时,资源才被回收;如果返回None,进程还在跑
| 子进程状态 | try_wait() 行为 |
是否回收资源 |
|---|---|---|
| 未退出 | 立即返回 Ok(None),不阻塞、不修改子进程状态、不回收任何资源 |
不回收 |
| 已退出 | 返回 Ok(Some(ExitStatus)),同时立即回收子进程的所有系统资源(PID、文件描述符等) |
回收 |
use chrono::prelude::Local;
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
fn main() {
let mut child = Command::new("sleep") // 用一个长运行的命令
.arg("5")
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start");
println!("satrt: {}", Local::now());
loop {
let res = child.try_wait().expect("Failed to wait on child");
println!("res: {:?}", res);
match res {
Some(status) => {
println!("{}", status);
break;
}
None => {
thread::sleep(Duration::from_millis(1000));
}
}
}
println!("end: {}", Local::now())
}
satrt: 2026-03-16 22:40:49.542281 +08:00
res: None
res: None
res: None
res: None
res: None
res: Some(ExitStatus(unix_wait_status(0)))
exit status: 0
end: 2026-03-16 22:40:54.560609 +08:00
6.3.4 child.kill()
强制终止正在运行的子进程,
kill()只负责发送信号,它不会等待进程彻底退出,也不会自动回收资源(PID)调用
kill()后,必须紧接着调用wait()或try_wait()来回收资源,否则会产生僵尸进程
use chrono::prelude::Local;
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
fn main() {
let mut child = Command::new("sleep") // 睡眠30秒
.arg("30")
.stdout(Stdio::piped())
.spawn()
.expect("Failed to start");
println!("satrt: {}", Local::now());
println!("PID: {}", child.id());
child.kill().expect("Failed to kill child");
// 睡眠10秒
thread::sleep(Duration::from_millis(10000));
println!("PID: {}", child.id());
println!("end: {}", Local::now());
let res = child.wait().expect("Failed to wait on child");
println!("res: {}", res);
}
satrt: 2026-03-16 22:46:32.346687 +08:00
PID: 5660
PID: 5660
end: 2026-03-16 22:46:42.351885 +08:00
res: signal: 9 (SIGKILL)
ps -d 5543
PID TTY TIME CMD
5543 ttys000 0:00.00 <defunct>
# 10秒之后执行
ps -d 5543
PID TTY TIME CMD
6.3.5 child.wait_with_output()
wait_with_output内部自动调用了wait资源回收
- 等待进程结束(相当于自动调用了 wait)。
- 捕获所有标准输出到内存中。
- 捕获所有标准错误到内存中。
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to start");
let s = child.wait_with_output().expect("err");
println!("{:#?}", s);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "Cargo.lock\nCargo.toml\na.sh\nsrc\ntarget\n",
stderr: "",
}
6.3.5.1 wait_with_output比较stdout/stderr
use std::process::{Command, Stdio};
use std::io::Read;
fn main() {
let mut child = Command::new("echo")
.arg("Hello")
.stdout(Stdio::piped()) // 必须手动设置管道
.stderr(Stdio::piped())
.spawn()
.expect("Failed");
// 1. 手动获取 stdout 句柄
let mut stdout = child.stdout.take().expect("No stdout");
let mut stderr = child.stderr.take().expect("No stderr");
// 2. 等待进程结束
let status = child.wait().expect("Failed");
// 3. 手动读取所有内容 (还需要处理 BufReader 避免死锁)
let mut out_buf = Vec::new();
let mut err_buf = Vec::new();
stdout.read_to_end(&mut out_buf).unwrap();
stderr.read_to_end(&mut err_buf).unwrap();
println!("Status: {:?}", status);
println!("Output: {}", String::from_utf8_lossy(&out_buf));
}
Status: ExitStatus(unix_wait_status(0))
Output: Cargo.lock
Cargo.toml
a.sh
src
target
6.3.6 输入stdint
6.3.6.1 stdint + Stdio::inherit
终端会一直等待输入,程序不会自动结束
- stdin(Stdio::inherit()), 将标准输入接到终端
- 命令(这里是grep)就会等待终端的输入
- grep是一个流处理工具,它会一直读取直到遇到 EOF(文件结束符)。
只要没收到EOF,它就认为"用户还没输完,我接着等" - 发送EOF:
- 自动发送EOF
- 读取文件,文件读完了系统自动发 EOF, 读取终端(键盘)
- 必须用户手动发送EOF
- Linux / macOS:按 Ctrl + D
- Windows:按 Ctrl + Z
- 自动发送EOF
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("grep")
.arg("a")
.stdin(Stdio::inherit())
.stdout(Stdio::piped())
.spawn()
.expect("grep failed to start");
let s = child.wait_with_output();
println!("{:?}", s);
}
aa
zz
az
# 需要按ctrl + d 结束输入
Ok(Output { status: ExitStatus(unix_wait_status(0)), stdout: "aa\naz\n", stderr: "" })
6.3.6.2 stdint + Stdio::piped
必须关闭stdin,否者会卡主
- 关闭方式
- 作用域关闭
{ let stdin = ...; } - 显示
drop(stdin) - let绑定
let _ = stdin;
- 作用域关闭
// Write必须引入这个trait才能用stdin.write_all
use std::{io::Write, process::{Command, Stdio}};
fn main() {
let mut child = Command::new("grep")
.arg("test")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("grep failed to start");
// 获取stdin + stdin写入 + stdin关闭(自动发送EOF)
{
let mut stdin = child.stdin.take().expect("Failed to get stdin");
// stdin输入内容
stdin.write_all(b"hello world\ntest abc\n").expect("write failed");
}
let s = child.wait_with_output();
println!("{:?}", s);
}
Ok(Output { status: ExitStatus(unix_wait_status(0)), stdout: "test abc\n", stderr: "" })
6.3.6.3 stdint + Stdio::from
效果
cat < test.log
123
456
789
use std::process::{Command, Stdio};
use std::fs::File;
use std::path::Path;
fn main() {
let file = File::open(Path::new("test.log")).unwrap();
let mut child = Command::new("cat")
.stdin(Stdio::from(file))
.stdout(Stdio::piped())
.spawn()
.expect("grep failed to start");
let s = child.wait_with_output().expect("failed to wait on child");
println!("{:#?}", s);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "123\n456\n789",
stderr: "",
}
6.3.7 输出stdout/stderr
6.3.7.1 stdout + Stdio::inherit
输出到终端(继承主程序)
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.spawn()
.expect("grep failed to start");
let s = child.wait_with_output();
println!("{:?}", s);
}
Cargo.lock Cargo.toml a.sh src target
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "",
stderr: "",
}
6.3.7.2 stdout + Stdio::piped
输出内容被rust捕获
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.stdin(Stdio::inherit())
.stdout(Stdio::piped())
.spawn()
.expect("grep failed to start");
let s = child.wait_with_output().expect("failed to wait on child");
println!("{:#?}", s);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "Cargo.lock\nCargo.toml\na.sh\nsrc\ntarget\n",
stderr: "",
}
6.3.7.3 stdout + Stdio::null
输出到/dev/null中
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.stdin(Stdio::inherit())
.stdout(Stdio::null())
.spawn()
.expect("grep failed to start");
let s = child.wait_with_output().expect("failed to wait on child");
println!("{:#?}", s);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "",
stderr: "",
}
6.3.7.4 stdout + Stdio::from
输出到文件只中
use std::process::{Command, Stdio};
use std::fs::File;
use std::io::Write;
fn main() {
let file = File::create("output.log").expect("无法创建文件");
let mut child = Command::new("ls")
.stdin(Stdio::inherit())
.stdout(Stdio::from(file))
.spawn()
.expect("grep failed to start");
let s = child.wait_with_output().expect("failed to wait on child");
println!("{:#?}", s);
}
Output {
status: ExitStatus(
unix_wait_status(
0,
),
),
stdout: "",
stderr: "",
}
7.Stdio说明
- Stdio::inherit(): 继承父进程的流
- Stdio::piped(): 创建管道,类似linux的|
- Stdio::null(): 连接到空设备。类似linux的/dev/null
- Stdio::from(file): 输入/输出到文件中
7.1 Stdio::inherit
输出内容直接显示在终端(脚本就是在终端执行的)
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.current_dir("./src")
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
.expect("Failed to start");
}
cargo run
a.sh main.rs
7.1.1 案例
// 情况 1:父进程在终端运行
// 子进程的输出 → 父进程的 stdout → 终端
Command::new("ls").stdout(Stdio::inherit()).spawn();
// 情况 2:父进程的 stdout 被重定向到文件
// 运行:cargo run > output.txt
// 子进程的输出 → 父进程的 stdout → output.txt 文件
Command::new("ls").stdout(Stdio::inherit()).spawn();
// 情况 3:父进程在管道中
// 运行:cargo run | grep xxx
// 子进程的输出 → 父进程的 stdout → 管道 → grep
Command::new("ls").stdout(Stdio::inherit()).spawn();
7.2 Stdio::piped()
输出的内容被rust捕获,不会输出到终端
use std::process::{Command, Stdio};
fn main() {
let mut child = Command::new("ls")
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("Failed to start");
}
7.3 Stdio::null()
Command::new("ls")
.stdout(Stdio::null()) // 扔掉标准输出
.stderr(Stdio::null()) // 扔掉错误输出
.output()
.expect("failed");
// 屏幕上干净如初
7.4 Stdio::from(File)
创建一个文件句柄,并指定文件路径
use std::fs::File;
let log_file = File::create("log.txt").unwrap();
Command::new("ls")
// 把 stdout 重定向到 log.txt 文件
.stdout(Stdio::from(log_file))
.spawn()
.unwrap();
8.案例
非阻塞启动与等待
use std::process::Command;
use std::thread;
use std::time::Duration;
fn main() {
println!("1. 启动耗时任务 (sleep 3秒)...");
// spawn 立即返回,不会等待 sleep 结束
let mut child = Command::new("sleep")
.arg("3")
.spawn()
.expect("Failed to start sleep command");
println!("2. 任务已启动,主程序继续工作...");
// 模拟主程序在做其他事情
thread::sleep(Duration::from_secs(1));
println!(" (主程序正在处理其他逻辑...)");
thread::sleep(Duration::from_secs(1));
println!(" (主程序还在忙...)");
println!("3. 现在等待子进程结束并获取结果...");
// 手动等待进程结束并捕获输出
let output = child.wait_with_output()
.expect("Failed to wait on child");
println!("4. 任务完成!状态: {}", output.status);
if !output.stdout.is_empty() {
println!("输出: {}", String::from_utf8_lossy(&output.stdout));
}
}
向子进程写入数据
use std::process::{Command, Stdio};
use std::io::Write;
fn main() {
println!("启动 cat 命令(它会回显你输入的内容)...");
let mut child = Command::new("cat")
.stdin(Stdio::piped()) // 关键:允许写入 stdin
.stdout(Stdio::piped()) // 关键:允许读取 stdout
.stderr(Stdio::inherit()) // 让错误信息直接打印到终端
.spawn()
.expect("Failed to spawn cat");
{
// 获取 stdin 的句柄
let mut stdin = child.stdin.take()
.expect("Failed to open stdin");
println!("正在向 cat 写入数据...");
// 写入数据
stdin.write_all(b"Hello from Rust!\n").expect("Failed to write");
stdin.write_all(b"Second line.\n").expect("Failed to write");
// 【至关重要】
// 必须显式 drop 掉 stdin,或者让它离开作用域。
// 这会给子进程发送 EOF (End Of File)。
// 如果不这样做,cat 会一直等待更多输入,永远不会结束,导致下面的 wait_with_output 卡死。
drop(stdin);
println!("输入已关闭 (发送 EOF)。");
}
// 现在可以安全地等待并获取回显
let output = child.wait_with_output()
.expect("Failed to wait");
println!("\n--- 捕获到的 stdout ---");
println!("{}", String::from_utf8_lossy(&output.stdout));
println!("-----------------------");
}
实时读取海量输出
use std::process::{Command, Stdio};
use std::io::{BufRead, BufReader};
use std::thread;
fn main() {
println!("启动 ping 命令 (实时读取输出)...");
let mut child = Command::new("ping")
.arg("-c")
.arg("5") // Linux: ping 5次; Windows 请用 "ping" "-n" "5" "google.com"
.arg("google.com")
.stdout(Stdio::piped()) // 必须 piped 才能读取
.stderr(Stdio::inherit())
.spawn()
.expect("Failed to spawn ping");
// 获取 stdout 并包装成 BufReader 以便逐行读取
let stdout = child.stdout.take()
.expect("Failed to open stdout");
let reader = BufReader::new(stdout);
// 在新线程中读取,防止阻塞主线程(可选,视需求而定)
// 这里直接在主线程演示
for line in reader.lines() {
match line {
Ok(l) => {
// 实时处理每一行
println!("[实时日志] {}", l);
// 你可以在这里加逻辑,比如发现 "unreachable" 就报警
if l.contains("unreachable") {
println!("⚠️ 检测到网络问题!");
}
},
Err(e) => eprintln!("读取行失败: {}", e),
}
}
// 等待进程彻底结束
let status = child.wait().expect("Failed to wait");
println!("\n进程结束,退出码: {}", status);
}
超时控制与强制杀死进程
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
use std::thread;
fn main() {
println!("启动一个可能会卡死的命令 (sleep 10)...");
let mut child = Command::new("sleep")
.arg("10") // 故意设长一点
.spawn()
.expect("Failed to spawn");
let timeout = Duration::from_secs(3); // 设定超时时间为 3 秒
let start_time = Instant::now();
println!("等待进程 (超时限制: {:?})...", timeout);
// 简单的轮询检查机制
loop {
// 检查是否已经超时
if start_time.elapsed() > timeout {
println!("⏰ 超时!正在杀死进程...");
child.kill().expect("Failed to kill process");
break;
}
// 检查进程是否已经结束
// try_wait() 是非阻塞的:如果没结束返回 Ok(None),结束了返回 Ok(Some(status))
match child.try_wait() {
Ok(Some(status)) => {
println!("进程正常结束,状态: {}", status);
return; // 退出
},
Ok(None) => {
// 进程还在跑,睡一小会儿再检查
thread::sleep(Duration::from_millis(500));
},
Err(e) => {
eprintln!("检查进程状态出错: {}", e);
break;
}
}
}
// 如果是因为超时被杀死的,我们需要再次 wait 来清理资源 (避免僵尸进程)
match child.wait() {
Ok(status) => println!("进程已被杀死,最终状态: {}", status),
Err(e) => eprintln!("等待被杀死的进程时出错: {}", e),
}
}
死锁
操作系统为每个进程的管道(Pipe)提供了一个固定大小的内核缓冲区
读写都需要使用缓冲区,
Stdio::piped()需要重新写入缓冲区被rust程序读取只要将
.stdout()设置为inherit、null或from(file),死锁现象就会彻底消失
// 错误示范:顺序执行,必死锁
// 前提my_program命令会输出大量信息到stdout/stderr
// 并且需要捕获被rust使用Stdio::piped()
let mut child = Command::new("my_program")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;
{
let mut stdin = child.stdin.take().unwrap();
// 1. 主进程试图写入大量数据 (例如 1MB)
// 如果子进程因为 stdout 满了而卡住,它就不会读 stdin。
// 如果 stdin 的管道缓冲区也满了,这里就会卡住!
stdin.write_all(large_data)?;
}
// 2. 主进程试图读取 stdout
// 但子进程早就卡在第一步(写 stdout)了,根本没机会读到 stdin 并继续运行产生更多输出或退出
let output = child.wait_with_output()?;
解决方法
- 使用多线程 : 开启一个线程专门负责读(stdout/stderr),主线程负责写(stdin)
- 使用第三方库:
duct库 (同步)tokio/async-std(异步场景)

浙公网安备 33010602011771号