rust
async fn run_cmd_async_out<I, S>(cmd: &str, args: I, timeout_s: u64, with_http_proxy: bool) -> Result<String>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
let mut cmd = tokio::process::Command::new(cmd);
// 让 sh 来运行命令,使得通配符能够被 shell 解析
let cmd = cmd.arg("-c").arg(cmd).args(args);
if with_http_proxy {
// 设置 HTTP 代理
if let Ok(c) = fs_read_line(CONF_HTTP_PROXY) {
cmd.env("http_proxy", c);
}
if let Ok(c) = fs_read_line(CONF_HTTPS_PROXY) {
cmd.env("https_proxy", c);
}
}
let mut child = cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().context("wait stdout/stderr failed")?;
match timeout(Duration::from_secs(timeout_s), child.wait()).await {
Ok(Ok(_)) => {
let output = child.wait_with_output().await?;
if !output.status.success() {
Err(anyhow!("run cmd failed, {}", String::from_utf8_lossy(output.stderr.as_slice())))
} else {
let status = String::from_utf8_lossy(output.stdout.as_slice()).to_string();
Ok(status)
}
}
Ok(Err(e)) => Err(anyhow!("running cmd error, {e}")),
Err(e) => {
let _ = child.start_kill();
let _ = child.wait().await;
Err(anyhow!("running cmd timeout, {e}"))
}
}
}
调用的示例 。
rust
system_async_run("sh", &vec!["-c", "rm -f /var/backups/*.pcap"]).await?;
解释:
在 Linux 中,当你使用命令如 rm -rf /var/volatile/*.pcap
时,*
是由 shell 进行扩展的,它将通配符替换为实际的文件名列表。在使用 tokio::process::Command
时,这种通配符扩展不会自动发生,因为 Command
直接调用的是操作系统的命令,而没有通过 shell 来进行扩展。
要解决这个问题,你可以让 tokio::process::Command
在运行时通过 shell 来执行该命令,从而使得 *
符号能够得到正确的扩展。你可以通过设置 sh
来实现这一点。
注: 妈的。好坑。折腾我半天。以为删除不了文件。