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

Process 启动另一个程序,然后退出,process,rust,Process,Rust,从一个用rust编写的程序a开始,我想启动一个程序B,有一个结束,让B正常运行,就像在a终止后从同一个shell手动启动一样 我目前的计划: use std::process::Command; pub fn execute(exe: &str, args: &[&str]) { Command::new(exe) .args(args) .spawn() .expect("failed to start exte

从一个用rust编写的程序a开始,我想启动一个程序B,有一个结束,让B正常运行,就像在a终止后从同一个shell手动启动一样

我目前的计划:

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(或其他程序)正常运行(即使没有任何窗口系统)?

以下是linux上的一个可行解决方案,使用函数的包装:


使用nix::unistd;
使用std::ffi::CString;
发布fn executev(参数:&[&str]){
让mut args:Vec=args.iter()
.map(| t | CString::new(*t).expect(“不是正确的CString”))
.收集();
unistd::execv(
&args[0],
&args,
).预期(“失败”);
}
fn main(){
executev(&[“/usr/bin/nvim”、“/home/dys/todo.txt”);
}

注意:这确实会启动另一个程序并退出,但请注意,替换当前进程意味着您正确关闭了打开的资源。如果您可以接受让您的程序保持活动状态,那么您可能希望按照Sven Marnach的建议等待。

经过进一步讨论,我们发现了实际问题:您要启动的程序应该位于前台,因此它可以从终端读取(在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()
}
该函数仅在出现错误时返回。否则,处理图像将被新图像替换。从shell的角度来看,它仍然是相同的过程,因此shell将等待您启动的命令完成

第二种方法的优势似乎很小。它在Windows上不工作,因为Windows不支持
exec()
和朋友。在运行该命令时,您将少使用一个进程,但实际上该进程的资源使用量应该很小——它不使用任何CPU,并且如果需要,可以交换内存页

原始答案 从一个用rust编写的程序a开始,我想启动一个程序B,有一个结束,让B正常运行,就像在a终止后从同一个shell手动启动一样

这或多或少是您的代码已经在做的事情。在Unix系统上,直接从shell启动的进程有一些不同之处,不过:

  • 新流程将不包括在shell的作业列表中,因此您不能使用shell的作业控制命令,如
    bg
    fg
  • 新进程将在后台运行,并且shell将在Rust程序退出后立即显示提示
这会失败,因为nvim是作为一个孩子启动的,并且在调用程序停止时立即终止

这是不正确的,无论是对还是对

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


这应该正是您的Rust代码所做的(以及它在我的Linux机器上运行时所做的)。另一方面,您的答案中的代码还有其他作用:它使用
execv()
用nvim替换生锈过程。实际上,进程不会立即停止,shell将被阻止,直到nvim退出。

父进程一死,Windows真的会杀死所有子进程吗?我在Windows上进行软件开发已经有二十年了,但我不记得这种行为了。@SvenMarnach我不知道Windows的情况,因为我现在没有任何可用的盒子。我的要求是让它在windows、linux和macos上工作。我将删除操作系统标签(可能会删除一段时间),以明确这不是一个特定于Windows的问题。我错过了还有一个Linux标签。当父进程退出时,Linux和Windows都不会杀死子进程。@SvenMarnach因为
process::spawn()
正在父进程和子进程之间设置一些管道。有一个
为true;埃科富;睡眠1;作为孩子完成,你应该看到失败。这既解释了为什么一个
os.setId()
在开始时可以工作,也解释了为什么它不能用
strace
(stdin不是pty)复制,如果这被确认为正确的解决方案,并且在确认后它已经在Windows上工作,然后我将清理这个快速编写的解决方案。这会做一些不同的事情:
execv()
函数在成功后不会返回,并且当前进程将被新进程替换。请注意,
Command::spawn()
也在引擎盖下使用
execv()
,但仅在
fork()
初始化子进程之后使用。我建议您了解您所看到的行为的可能根本原因。@SvenMarnach我的理解是避免使用
fork
是目标,而简单地等待子进程也会起作用,并避免使用特定于平台的代码。“以及它在我的Linux机器上运行时的作用”你有没有尝试过像vim这样的真正的终端程序,而不仅仅是睡眠?你的解释很有趣,但我不知道你是如何解决这个问题的。@DenysSéguret我没有安装“nvim”,所以我没有尝试,因为我尝试了
bash-c',虽然是真的;做一个回声;睡眠2;“完成”
,但它仍在后台运行。@DenysSéguret我还建议了一个简单的解决方案,其行为与您的答案注释中的答案类似–具体地说,只需等待子流程,而不是立即退出即可。我没有在这个答案中包括这一点,因为它与问题中的要求(要求调用程序立即退出)有直接冲突。谢谢你的回答。“接受或不接受只会在更多的研究之后才会出现。”DenysSéguret我调整了答案来解释这一发现
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()
}