我要用锈写的程序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 (或其他程序)正确运行(即使没有任何窗口系统)?
答案 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启动的进程相比,存在一些差异:
bg
和fg
。此操作失败是因为 nvim 是从小启动的,并在调用程序停止时被杀死。
我该如何编写 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
。