Запустите другую программу, затем выйдите - PullRequest
0 голосов
/ 26 ноября 2018

Из программы 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 голосов
/ 26 ноября 2018

После дальнейшего обсуждения мы определили фактическую проблему: запускаемая вами программа должна оставаться на переднем плане, чтобы она могла читать с терминала (что фоновые процессы не могут делать в 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() familiy, чтобы заменить изображение родительского процесса на изображение дочернего процесса:

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, поскольку Windows не поддерживает exec() и друзей.Во время выполнения команды у вас будет на один процесс меньше, но на практике использование этого процесса должно быть небольшим - он не использует ЦП, и страницы памяти можно при необходимости заменить.

ОригиналОтвет

Из программы A, написанной на ржавчине, я хочу запустить программу B, завершить ее A и, как правило, запустить B так же, как если бы он был запущен вручную из той же оболочки сразу после завершенияA.

Это более или менее то, что ваш код уже делает.Однако у процесса, запускаемого непосредственно из оболочки в системах Unix, есть несколько отличий:

  • Новый процесс не будет включен в список заданий оболочки, поэтому вы не можете использовать задание оболочкиуправляющие команды, такие как bg и fg.
  • Новый процесс будет выполняться в фоновом режиме, и оболочка сразу же покажет приглашение после выхода из программ Rust.

Это происходит сбой, потому что nvim запускается как дочерний и уничтожается, как только вызывающая программа останавливается.

Это не так, ни для Unix ни для Windows .

Как мне написать выполнить , чтобы программа вызывающего абонента немедленно остановилась и позволила nvim (или другую программу) правильно работать (даже без оконной системы)?

Это должно быть именно то, что делает ваш код Rust (и то, что он делает при запуске на моей машине с Linux).Код в вашем ответе, с другой стороны, делает что-то еще: он использует execv() до , заменяет процесс Rust на nvim.По сути, процесс не немедленно останавливается, а остаток оболочки блокируется до выхода nvim.

0 голосов
/ 26 ноября 2018

Вот рабочее решение на 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"]);
}

Примечание: Это делает Запустите другую программу и выйдите, но будьте осторожны, так как замена текущего процесса подразумевает, что вы правильно закрыли открытые ресурсы.Если вы можете согласиться с тем, чтобы ваша программа оставалась в живых, вы, вероятно, захотите wait, как предложил Свен Марнах.

...