`std::process:Command` 在使用 'bash -c' 时没有返回 `Err()` 结果。

4

我试图理解当使用bash -c表达式运行命令时,为什么没有得到Err()结果。

以下是一个示例和输出。我期望out2out3都会是Err(),但实际上out3Ok(),并带有失败的状态信息。

我正在使用bash -c从给定的字符串执行命令,它很容易实现。

使用bash -c语法能否获得Err()结果?

#![allow(unused)]

use std::process::{Command, Output};

fn main() {
    let out1 = Command::new("bash").arg("-c").arg("ls").output();
    println!("out1: {:?}", out1);

    let out2 = Command::new("wrongcommand").arg("-c").arg("ls").output();
    println!("out2: {:?}", out2);

    let out3 = Command::new("bash").arg("-c").arg("wrongcommand").output();
    println!("out3: {:?}", out3);
}

输出:

out1: Ok(Output { status: ExitStatus(ExitStatus(0)), stdout: "Cargo.lock\nCargo.toml\ncrate-information.json\nsrc\ntarget\n", stderr: "" })
out2: Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })
out3: Ok(Output { status: ExitStatus(ExitStatus(32512)), stdout: "", stderr: "bash: wrongcommand: command not found\n" })

我尝试了命令行并执行了以下操作:

$ bash -c wrongcommand

并且
$ wrongcommand

两者返回相同的退出代码(127)。所以我预计命令的失败方式是一样的。


稍微编辑了一下我的答案,增加了关于如何捕获和重定向那个错误的 “Ok” 的更多信息。 - Sébastien Renauld
1个回答

5

这个可以很容易地解释。跟随错误代码列表

out1因为显而易见的原因是Ok

out2Err 类型,因为Command直接查找进程,无法找到它,并返回ENOENT(代码2)。这个错误发生在Rust内部,实际上没有执行任何操作。

out3Ok,因为Command运行的进程是bash,并返回其状态。在这种情况下,它不会找到命令,因此按照bash的状态,它将返回127。但是,这并不容易,因为ExitStatus中包含了额外的信息层。

  • 最高位是触发返回的信号
  • 最低的8位是进程的返回状态

而且毫不奇怪,如果我们将32512右移8位(它是一个u16),我们得到... 127!

要点很简单:

  • Err肯定意味着进程没有运行
  • Ok表示主进程已运行,但您需要检查ExitStatus::success()以确认它实际上确实运行了(即退出状态为0)

你可以像这样恢复它:

Command::new("bash").arg("-c").arg("wrongcommand").output().and_then(|r| match r.status.success() {
  true => Ok(r),
  false => Err(io::Error::new(io::ErrorKind::InvalidData, "Process error"))
});

您可以在playground上测试。 success()是一个相当可靠的指示器,它用于检查子进程是否成功运行;它只检查退出状态的最低8位是否为非零。
显然,如果进程在成功时返回非零值,则这并没有帮助,但这是另一个问题。

谢谢你的回答。是的,我确实意识到bash是返回状态的命令。我的困惑在于,在终端中,两种方式都返回127,所以我期望它们有相同的行为。现在,我将命令执行包装起来,并根据状态返回Ok()或Err()。 - Matias
一切都好了 :-) 只要记住,如果你需要状态,你可以通过位移将其取回。 - Sébastien Renauld

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接