启动另一个程序,然后退出

时间:2018-11-26 09:13:50

标签: process rust

我要用锈写的程序A启动程序B,结束A,让B正常运行,就像它是在终止A之后从同一外壳手动启动一样。

我当前的程序:

use std::process::Command;

pub fn execute(exe: &str, args: &[&str]) {
    Command::new(exe)
        .args(args)
        .spawn()
        .expect("failed to start external executable");
}

fn main() {
    execute("/usr/bin/nvim", &["/home/dys/todo.txt"]);
}

这失败。 nvim 是从小启动的,并且在调用程序停止运行后便无法工作。

如何编写execute,以便调用程序立即停止并使 nvim (或其他程序)正确运行(即使没有任何窗口系统)?

2 个答案:

答案 0 :(得分:2)

在进一步讨论之后,我们确定了实际的问题:正在启动的程序应该保留在前台,因此可以从终端读取(Unix上后台进程无法执行)。

有两种方法可以实现此目的。第一个也是最简单的方法是在父进程退出之前等待子进程:

use std::process::{Command, ExitStatus};
use std::io::Result;

pub fn execute(exe: &str, args: &[&str]) -> Result<ExitStatus> {
    Command::new(exe).args(args).spawn()?.wait()
}

这可以确保进程(父进程和子进程)保持在前台,因为外壳程序正在等待父进程,因此子进程可以从终端读取。

如果由于某些原因您不能让子进程在运行时仍无法继续运行,则需要依赖于平台的代码。在Unix上,您可以使用exec()家族中的一些syscall来将父进程的映像替换为子进程的映像:

use std::process::Command;
use std::os::unix::process::CommandExt;
use std::io::Error;

pub fn execute(exe: &str, args: &[&str]) -> Error {
    Command::new(exe).args(args).exec()
}

该函数仅在出现错误时返回。否则,过程映像将被新映像替换。从外壳程序的角度来看,它仍然是相同的过程,因此外壳程序将等待您启动的命令完成。

第二种方法的优点似乎微不足道。由于Windows不支持exec()和朋友,因此它在Windows上不起作用。运行该命令时,您的进程将减少一个,但是实际上该进程的资源使用应很小–它不使用任何CPU,并且可以在必要时换出内存页。

原始答案

  

我要用锈写的程序A启动程序B,结束A,让B正常运行,就像它是在终止A之后从同一外壳手动启动一样。

这或多或少是您的代码已经在做的事情。但是,与直接从Unix系统上的Shell启动的进程相比,存在一些差异:

  • 新进程不会包含在Shell的作业列表中,因此您不能使用Shell的作业控制命令,例如bgfg
  • 新进程将在后台运行,Rust程序退出后,shell将立即显示提示。
  

此操作失败是因为 nvim 是从小启动的,并在调用程序停止时被杀死。

对于UnixWindows来说都是不正确的。

  

我该如何编写 execute ,以便调用者程序立即停止并使 nvim (或其他程序)正常运行(即使没有任何窗口系统)?

这应该恰好是您的Rust代码正在做的事情(以及在我的Linux机器上运行时的样子)。另一方面,您答案中的代码还有其他作用:它使用execv()用nvim 替换 Rust进程。实际上,该进程不会立即停止,并且外壳保持阻塞状态,直到nvim退出。

答案 1 :(得分:1)

这是Linux上的一种可行解决方案,它使用execv函数的包装:


use nix::unistd;
use std::ffi::CString;

pub fn executev(args: &[&str]) {
    let mut args: Vec<CString> = args.iter()
        .map(|t| CString::new(*t).expect("not a proper CString"))
        .collect();
    unistd::execv(
        &args[0],
        &args,
    ).expect("failed");
}

fn main() {
    executev(&["/usr/bin/nvim", "/home/dys/todo.txt"]);
}

注意:此操作确实启动另一个程序并退出,但请注意,替换当前进程意味着您已正确关闭了开放资源。如果您可以接受让程序继续运行,那么您可能想要按照Sven Marnach的建议wait