Start another program then quit












6















From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.



My current program:



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"]);
}


This fails. nvim is launched as a child and is non-working as soon as the calling program stops.



How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system) ?










share|improve this question

























  • Does Windows really kill all child processes as soon as the parent process dies? It's been two decades since I did software development on Windows, but I don't remember this behaviour.

    – Sven Marnach
    Nov 26 '18 at 9:35











  • @SvenMarnach I don't know for Windows as I don't have any box available right now. My requirement is to have it work on windows, linux and macos. I'll remove the OS tags (for a time maybe) to make it clear this isn't a Windows specific question.

    – Denys Séguret
    Nov 26 '18 at 9:37













  • I missed there was also a Linux tag. Neither Linux nor Windows kill the child process when the parent process exits.

    – Sven Marnach
    Nov 26 '18 at 9:46






  • 1





    @SvenMarnach because process::spawn() is setting up some pipes between parent and child. Have a while true; echo foo; sleep 1; done as the child, and you should see the failure.

    – Florian Margaine
    Nov 26 '18 at 11:05






  • 1





    Which explains both why an os.setsid() in the beginning the process works, and why it's not reproducible with strace (stdin is not a pty)

    – Florian Margaine
    Nov 26 '18 at 14:10
















6















From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.



My current program:



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"]);
}


This fails. nvim is launched as a child and is non-working as soon as the calling program stops.



How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system) ?










share|improve this question

























  • Does Windows really kill all child processes as soon as the parent process dies? It's been two decades since I did software development on Windows, but I don't remember this behaviour.

    – Sven Marnach
    Nov 26 '18 at 9:35











  • @SvenMarnach I don't know for Windows as I don't have any box available right now. My requirement is to have it work on windows, linux and macos. I'll remove the OS tags (for a time maybe) to make it clear this isn't a Windows specific question.

    – Denys Séguret
    Nov 26 '18 at 9:37













  • I missed there was also a Linux tag. Neither Linux nor Windows kill the child process when the parent process exits.

    – Sven Marnach
    Nov 26 '18 at 9:46






  • 1





    @SvenMarnach because process::spawn() is setting up some pipes between parent and child. Have a while true; echo foo; sleep 1; done as the child, and you should see the failure.

    – Florian Margaine
    Nov 26 '18 at 11:05






  • 1





    Which explains both why an os.setsid() in the beginning the process works, and why it's not reproducible with strace (stdin is not a pty)

    – Florian Margaine
    Nov 26 '18 at 14:10














6












6








6


0






From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.



My current program:



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"]);
}


This fails. nvim is launched as a child and is non-working as soon as the calling program stops.



How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system) ?










share|improve this question
















From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.



My current program:



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"]);
}


This fails. nvim is launched as a child and is non-working as soon as the calling program stops.



How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system) ?







process rust






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 26 '18 at 16:45







Denys Séguret

















asked Nov 26 '18 at 9:13









Denys SéguretDenys Séguret

281k56589605




281k56589605













  • Does Windows really kill all child processes as soon as the parent process dies? It's been two decades since I did software development on Windows, but I don't remember this behaviour.

    – Sven Marnach
    Nov 26 '18 at 9:35











  • @SvenMarnach I don't know for Windows as I don't have any box available right now. My requirement is to have it work on windows, linux and macos. I'll remove the OS tags (for a time maybe) to make it clear this isn't a Windows specific question.

    – Denys Séguret
    Nov 26 '18 at 9:37













  • I missed there was also a Linux tag. Neither Linux nor Windows kill the child process when the parent process exits.

    – Sven Marnach
    Nov 26 '18 at 9:46






  • 1





    @SvenMarnach because process::spawn() is setting up some pipes between parent and child. Have a while true; echo foo; sleep 1; done as the child, and you should see the failure.

    – Florian Margaine
    Nov 26 '18 at 11:05






  • 1





    Which explains both why an os.setsid() in the beginning the process works, and why it's not reproducible with strace (stdin is not a pty)

    – Florian Margaine
    Nov 26 '18 at 14:10



















  • Does Windows really kill all child processes as soon as the parent process dies? It's been two decades since I did software development on Windows, but I don't remember this behaviour.

    – Sven Marnach
    Nov 26 '18 at 9:35











  • @SvenMarnach I don't know for Windows as I don't have any box available right now. My requirement is to have it work on windows, linux and macos. I'll remove the OS tags (for a time maybe) to make it clear this isn't a Windows specific question.

    – Denys Séguret
    Nov 26 '18 at 9:37













  • I missed there was also a Linux tag. Neither Linux nor Windows kill the child process when the parent process exits.

    – Sven Marnach
    Nov 26 '18 at 9:46






  • 1





    @SvenMarnach because process::spawn() is setting up some pipes between parent and child. Have a while true; echo foo; sleep 1; done as the child, and you should see the failure.

    – Florian Margaine
    Nov 26 '18 at 11:05






  • 1





    Which explains both why an os.setsid() in the beginning the process works, and why it's not reproducible with strace (stdin is not a pty)

    – Florian Margaine
    Nov 26 '18 at 14:10

















Does Windows really kill all child processes as soon as the parent process dies? It's been two decades since I did software development on Windows, but I don't remember this behaviour.

– Sven Marnach
Nov 26 '18 at 9:35





Does Windows really kill all child processes as soon as the parent process dies? It's been two decades since I did software development on Windows, but I don't remember this behaviour.

– Sven Marnach
Nov 26 '18 at 9:35













@SvenMarnach I don't know for Windows as I don't have any box available right now. My requirement is to have it work on windows, linux and macos. I'll remove the OS tags (for a time maybe) to make it clear this isn't a Windows specific question.

– Denys Séguret
Nov 26 '18 at 9:37







@SvenMarnach I don't know for Windows as I don't have any box available right now. My requirement is to have it work on windows, linux and macos. I'll remove the OS tags (for a time maybe) to make it clear this isn't a Windows specific question.

– Denys Séguret
Nov 26 '18 at 9:37















I missed there was also a Linux tag. Neither Linux nor Windows kill the child process when the parent process exits.

– Sven Marnach
Nov 26 '18 at 9:46





I missed there was also a Linux tag. Neither Linux nor Windows kill the child process when the parent process exits.

– Sven Marnach
Nov 26 '18 at 9:46




1




1





@SvenMarnach because process::spawn() is setting up some pipes between parent and child. Have a while true; echo foo; sleep 1; done as the child, and you should see the failure.

– Florian Margaine
Nov 26 '18 at 11:05





@SvenMarnach because process::spawn() is setting up some pipes between parent and child. Have a while true; echo foo; sleep 1; done as the child, and you should see the failure.

– Florian Margaine
Nov 26 '18 at 11:05




1




1





Which explains both why an os.setsid() in the beginning the process works, and why it's not reproducible with strace (stdin is not a pty)

– Florian Margaine
Nov 26 '18 at 14:10





Which explains both why an os.setsid() in the beginning the process works, and why it's not reproducible with strace (stdin is not a pty)

– Florian Margaine
Nov 26 '18 at 14:10












2 Answers
2






active

oldest

votes


















2














After further discussion, we identified the actual problem: The program you are launching is supposed to stay in the foreground, so it can read from the terminal (which background processes can't do on Unix).



There are two ways to achieve this. The first, and easiest, is to wait for the child process before the parent process exits:



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()
}


This ensures the processes (parent and child) stay in the foreground, since the shell is waiting for the parent process, so the child process can read from the terminal.



If for some reason you can't afford the parent process to linger on while the child process is running, you need platform-dependent code. On Unix, you can use some syscall from the exec() familiy to replace the image of the parent process with the image of the child process:



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()
}


The function only returns if there is an error. Otherwise, the process image is replaced by the new image. From the viewpoint of the shell, it's still the same process, so the shell will wait for the command you launched to finish.



The advantages of the second approach seem slim. It does not work on Windows, since Windows does not support exec() and friends. You will have one less process around while running the command, but the resource usage of that process should be small in practice – it does not use any CPU, and the memory pages can be swapped out if necessary.



Original Answer




From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.




This is more or less what your code is already doing. There are a few differences to a process launched directly from the shell on Unix systems, though:




  • The new process will not be included in the shell's job list, so you can't use the shell's job control commands like bg and fg.

  • The new process will run in the background, and the shell will immediately show a prompt after the Rust programs exits.



This fails because nvim is launched as a child and is killed as soon as the calling program stops.




This is not true, neither for Unix nor for Windows.




How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system)?




This should be exactly what your Rust code is doing (and what it does when run on my Linux machine). The code in your answer, on the other hand, does something else: It uses execv() to replace the Rust process with nvim. In effect, the process does not immediately stop, and the shell remaind blocked until nvim exits.






share|improve this answer


























  • "and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

    – Denys Séguret
    Nov 26 '18 at 11:20













  • @DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

    – Sven Marnach
    Nov 26 '18 at 11:48











  • @DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

    – Sven Marnach
    Nov 26 '18 at 12:03











  • thanks for the answer. An accept or not will only come after some more studies.

    – Denys Séguret
    Nov 26 '18 at 13:18











  • @DenysSéguret I adapted the answer to account for the findings in the comments.

    – Sven Marnach
    Nov 28 '18 at 15:28



















1














Here's a working solution on linux, using a wrapping ot the execv function:




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"]);
}


Note: This does start another program and quit but be wary that replacing the current process implies you properly closed open resources. If you can accept having your program kept alive, you probably want to wait as suggested by Sven Marnach.






share|improve this answer


























  • If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

    – Denys Séguret
    Nov 26 '18 at 10:07











  • This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

    – Sven Marnach
    Nov 26 '18 at 10:19











  • @SvenMarnach My understanding is that avoiding fork was the goal

    – Denys Séguret
    Nov 26 '18 at 10:19











  • Then simply waiting for the subprocess would also work, and avoid platform-specific code.

    – Sven Marnach
    Nov 26 '18 at 10:22












Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53477846%2fstart-another-program-then-quit%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









2














After further discussion, we identified the actual problem: The program you are launching is supposed to stay in the foreground, so it can read from the terminal (which background processes can't do on Unix).



There are two ways to achieve this. The first, and easiest, is to wait for the child process before the parent process exits:



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()
}


This ensures the processes (parent and child) stay in the foreground, since the shell is waiting for the parent process, so the child process can read from the terminal.



If for some reason you can't afford the parent process to linger on while the child process is running, you need platform-dependent code. On Unix, you can use some syscall from the exec() familiy to replace the image of the parent process with the image of the child process:



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()
}


The function only returns if there is an error. Otherwise, the process image is replaced by the new image. From the viewpoint of the shell, it's still the same process, so the shell will wait for the command you launched to finish.



The advantages of the second approach seem slim. It does not work on Windows, since Windows does not support exec() and friends. You will have one less process around while running the command, but the resource usage of that process should be small in practice – it does not use any CPU, and the memory pages can be swapped out if necessary.



Original Answer




From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.




This is more or less what your code is already doing. There are a few differences to a process launched directly from the shell on Unix systems, though:




  • The new process will not be included in the shell's job list, so you can't use the shell's job control commands like bg and fg.

  • The new process will run in the background, and the shell will immediately show a prompt after the Rust programs exits.



This fails because nvim is launched as a child and is killed as soon as the calling program stops.




This is not true, neither for Unix nor for Windows.




How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system)?




This should be exactly what your Rust code is doing (and what it does when run on my Linux machine). The code in your answer, on the other hand, does something else: It uses execv() to replace the Rust process with nvim. In effect, the process does not immediately stop, and the shell remaind blocked until nvim exits.






share|improve this answer


























  • "and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

    – Denys Séguret
    Nov 26 '18 at 11:20













  • @DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

    – Sven Marnach
    Nov 26 '18 at 11:48











  • @DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

    – Sven Marnach
    Nov 26 '18 at 12:03











  • thanks for the answer. An accept or not will only come after some more studies.

    – Denys Séguret
    Nov 26 '18 at 13:18











  • @DenysSéguret I adapted the answer to account for the findings in the comments.

    – Sven Marnach
    Nov 28 '18 at 15:28
















2














After further discussion, we identified the actual problem: The program you are launching is supposed to stay in the foreground, so it can read from the terminal (which background processes can't do on Unix).



There are two ways to achieve this. The first, and easiest, is to wait for the child process before the parent process exits:



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()
}


This ensures the processes (parent and child) stay in the foreground, since the shell is waiting for the parent process, so the child process can read from the terminal.



If for some reason you can't afford the parent process to linger on while the child process is running, you need platform-dependent code. On Unix, you can use some syscall from the exec() familiy to replace the image of the parent process with the image of the child process:



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()
}


The function only returns if there is an error. Otherwise, the process image is replaced by the new image. From the viewpoint of the shell, it's still the same process, so the shell will wait for the command you launched to finish.



The advantages of the second approach seem slim. It does not work on Windows, since Windows does not support exec() and friends. You will have one less process around while running the command, but the resource usage of that process should be small in practice – it does not use any CPU, and the memory pages can be swapped out if necessary.



Original Answer




From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.




This is more or less what your code is already doing. There are a few differences to a process launched directly from the shell on Unix systems, though:




  • The new process will not be included in the shell's job list, so you can't use the shell's job control commands like bg and fg.

  • The new process will run in the background, and the shell will immediately show a prompt after the Rust programs exits.



This fails because nvim is launched as a child and is killed as soon as the calling program stops.




This is not true, neither for Unix nor for Windows.




How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system)?




This should be exactly what your Rust code is doing (and what it does when run on my Linux machine). The code in your answer, on the other hand, does something else: It uses execv() to replace the Rust process with nvim. In effect, the process does not immediately stop, and the shell remaind blocked until nvim exits.






share|improve this answer


























  • "and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

    – Denys Séguret
    Nov 26 '18 at 11:20













  • @DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

    – Sven Marnach
    Nov 26 '18 at 11:48











  • @DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

    – Sven Marnach
    Nov 26 '18 at 12:03











  • thanks for the answer. An accept or not will only come after some more studies.

    – Denys Séguret
    Nov 26 '18 at 13:18











  • @DenysSéguret I adapted the answer to account for the findings in the comments.

    – Sven Marnach
    Nov 28 '18 at 15:28














2












2








2







After further discussion, we identified the actual problem: The program you are launching is supposed to stay in the foreground, so it can read from the terminal (which background processes can't do on Unix).



There are two ways to achieve this. The first, and easiest, is to wait for the child process before the parent process exits:



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()
}


This ensures the processes (parent and child) stay in the foreground, since the shell is waiting for the parent process, so the child process can read from the terminal.



If for some reason you can't afford the parent process to linger on while the child process is running, you need platform-dependent code. On Unix, you can use some syscall from the exec() familiy to replace the image of the parent process with the image of the child process:



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()
}


The function only returns if there is an error. Otherwise, the process image is replaced by the new image. From the viewpoint of the shell, it's still the same process, so the shell will wait for the command you launched to finish.



The advantages of the second approach seem slim. It does not work on Windows, since Windows does not support exec() and friends. You will have one less process around while running the command, but the resource usage of that process should be small in practice – it does not use any CPU, and the memory pages can be swapped out if necessary.



Original Answer




From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.




This is more or less what your code is already doing. There are a few differences to a process launched directly from the shell on Unix systems, though:




  • The new process will not be included in the shell's job list, so you can't use the shell's job control commands like bg and fg.

  • The new process will run in the background, and the shell will immediately show a prompt after the Rust programs exits.



This fails because nvim is launched as a child and is killed as soon as the calling program stops.




This is not true, neither for Unix nor for Windows.




How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system)?




This should be exactly what your Rust code is doing (and what it does when run on my Linux machine). The code in your answer, on the other hand, does something else: It uses execv() to replace the Rust process with nvim. In effect, the process does not immediately stop, and the shell remaind blocked until nvim exits.






share|improve this answer















After further discussion, we identified the actual problem: The program you are launching is supposed to stay in the foreground, so it can read from the terminal (which background processes can't do on Unix).



There are two ways to achieve this. The first, and easiest, is to wait for the child process before the parent process exits:



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()
}


This ensures the processes (parent and child) stay in the foreground, since the shell is waiting for the parent process, so the child process can read from the terminal.



If for some reason you can't afford the parent process to linger on while the child process is running, you need platform-dependent code. On Unix, you can use some syscall from the exec() familiy to replace the image of the parent process with the image of the child process:



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()
}


The function only returns if there is an error. Otherwise, the process image is replaced by the new image. From the viewpoint of the shell, it's still the same process, so the shell will wait for the command you launched to finish.



The advantages of the second approach seem slim. It does not work on Windows, since Windows does not support exec() and friends. You will have one less process around while running the command, but the resource usage of that process should be small in practice – it does not use any CPU, and the memory pages can be swapped out if necessary.



Original Answer




From a program A written in rust, I want to start a program B, have A end, and have B normally run just like if it was manually launched from the same shell just after termination of A.




This is more or less what your code is already doing. There are a few differences to a process launched directly from the shell on Unix systems, though:




  • The new process will not be included in the shell's job list, so you can't use the shell's job control commands like bg and fg.

  • The new process will run in the background, and the shell will immediately show a prompt after the Rust programs exits.



This fails because nvim is launched as a child and is killed as soon as the calling program stops.




This is not true, neither for Unix nor for Windows.




How can I write execute so the caller program immediately stops and lets nvim (or another program) properly run (even without any windowing system)?




This should be exactly what your Rust code is doing (and what it does when run on my Linux machine). The code in your answer, on the other hand, does something else: It uses execv() to replace the Rust process with nvim. In effect, the process does not immediately stop, and the shell remaind blocked until nvim exits.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 28 '18 at 15:28

























answered Nov 26 '18 at 11:04









Sven MarnachSven Marnach

358k79757700




358k79757700













  • "and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

    – Denys Séguret
    Nov 26 '18 at 11:20













  • @DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

    – Sven Marnach
    Nov 26 '18 at 11:48











  • @DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

    – Sven Marnach
    Nov 26 '18 at 12:03











  • thanks for the answer. An accept or not will only come after some more studies.

    – Denys Séguret
    Nov 26 '18 at 13:18











  • @DenysSéguret I adapted the answer to account for the findings in the comments.

    – Sven Marnach
    Nov 28 '18 at 15:28



















  • "and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

    – Denys Séguret
    Nov 26 '18 at 11:20













  • @DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

    – Sven Marnach
    Nov 26 '18 at 11:48











  • @DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

    – Sven Marnach
    Nov 26 '18 at 12:03











  • thanks for the answer. An accept or not will only come after some more studies.

    – Denys Séguret
    Nov 26 '18 at 13:18











  • @DenysSéguret I adapted the answer to account for the findings in the comments.

    – Sven Marnach
    Nov 28 '18 at 15:28

















"and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

– Denys Séguret
Nov 26 '18 at 11:20







"and what it does when run on my Linux machine" Did you try with a real terminal program like vim and not just a sleep ? Your explanations are interesting but I don't see how you solve the problem.

– Denys Séguret
Nov 26 '18 at 11:20















@DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

– Sven Marnach
Nov 26 '18 at 11:48





@DenysSéguret I don't have "nvim" installed, so I didn't try that, by I tried bash -c 'while true; do echo a; sleep 2; done', and it still keeps running in the background.

– Sven Marnach
Nov 26 '18 at 11:48













@DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

– Sven Marnach
Nov 26 '18 at 12:03





@DenysSéguret I also suggested a simple solution that behaves similar to your answer in the comments to your answer – specifically, simply wait for the subprocess instead of immediately exiting. I did not include this in this answer, since it is in direct conflict with the requirements in the question (which asks that the caller program exits immediately).

– Sven Marnach
Nov 26 '18 at 12:03













thanks for the answer. An accept or not will only come after some more studies.

– Denys Séguret
Nov 26 '18 at 13:18





thanks for the answer. An accept or not will only come after some more studies.

– Denys Séguret
Nov 26 '18 at 13:18













@DenysSéguret I adapted the answer to account for the findings in the comments.

– Sven Marnach
Nov 28 '18 at 15:28





@DenysSéguret I adapted the answer to account for the findings in the comments.

– Sven Marnach
Nov 28 '18 at 15:28













1














Here's a working solution on linux, using a wrapping ot the execv function:




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"]);
}


Note: This does start another program and quit but be wary that replacing the current process implies you properly closed open resources. If you can accept having your program kept alive, you probably want to wait as suggested by Sven Marnach.






share|improve this answer


























  • If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

    – Denys Séguret
    Nov 26 '18 at 10:07











  • This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

    – Sven Marnach
    Nov 26 '18 at 10:19











  • @SvenMarnach My understanding is that avoiding fork was the goal

    – Denys Séguret
    Nov 26 '18 at 10:19











  • Then simply waiting for the subprocess would also work, and avoid platform-specific code.

    – Sven Marnach
    Nov 26 '18 at 10:22
















1














Here's a working solution on linux, using a wrapping ot the execv function:




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"]);
}


Note: This does start another program and quit but be wary that replacing the current process implies you properly closed open resources. If you can accept having your program kept alive, you probably want to wait as suggested by Sven Marnach.






share|improve this answer


























  • If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

    – Denys Séguret
    Nov 26 '18 at 10:07











  • This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

    – Sven Marnach
    Nov 26 '18 at 10:19











  • @SvenMarnach My understanding is that avoiding fork was the goal

    – Denys Séguret
    Nov 26 '18 at 10:19











  • Then simply waiting for the subprocess would also work, and avoid platform-specific code.

    – Sven Marnach
    Nov 26 '18 at 10:22














1












1








1







Here's a working solution on linux, using a wrapping ot the execv function:




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"]);
}


Note: This does start another program and quit but be wary that replacing the current process implies you properly closed open resources. If you can accept having your program kept alive, you probably want to wait as suggested by Sven Marnach.






share|improve this answer















Here's a working solution on linux, using a wrapping ot the execv function:




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"]);
}


Note: This does start another program and quit but be wary that replacing the current process implies you properly closed open resources. If you can accept having your program kept alive, you probably want to wait as suggested by Sven Marnach.







share|improve this answer














share|improve this answer



share|improve this answer








edited Jan 23 at 12:53

























answered Nov 26 '18 at 10:06









Denys SéguretDenys Séguret

281k56589605




281k56589605













  • If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

    – Denys Séguret
    Nov 26 '18 at 10:07











  • This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

    – Sven Marnach
    Nov 26 '18 at 10:19











  • @SvenMarnach My understanding is that avoiding fork was the goal

    – Denys Séguret
    Nov 26 '18 at 10:19











  • Then simply waiting for the subprocess would also work, and avoid platform-specific code.

    – Sven Marnach
    Nov 26 '18 at 10:22



















  • If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

    – Denys Séguret
    Nov 26 '18 at 10:07











  • This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

    – Sven Marnach
    Nov 26 '18 at 10:19











  • @SvenMarnach My understanding is that avoiding fork was the goal

    – Denys Séguret
    Nov 26 '18 at 10:19











  • Then simply waiting for the subprocess would also work, and avoid platform-specific code.

    – Sven Marnach
    Nov 26 '18 at 10:22

















If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

– Denys Séguret
Nov 26 '18 at 10:07





If this is confirmed to be the right solution, and after confirmation it was already working on Windows, then I'll clean this fast written solution up.

– Denys Séguret
Nov 26 '18 at 10:07













This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

– Sven Marnach
Nov 26 '18 at 10:19





This does something different: the execv() function does not return on success, and the current process is replaced by the new process. Note that Command::spawn() also uses execv() under the hood, but only after fork()ing a child process. I recommend looking into this answer to understand possible root causes of the behaviour you saw.

– Sven Marnach
Nov 26 '18 at 10:19













@SvenMarnach My understanding is that avoiding fork was the goal

– Denys Séguret
Nov 26 '18 at 10:19





@SvenMarnach My understanding is that avoiding fork was the goal

– Denys Séguret
Nov 26 '18 at 10:19













Then simply waiting for the subprocess would also work, and avoid platform-specific code.

– Sven Marnach
Nov 26 '18 at 10:22





Then simply waiting for the subprocess would also work, and avoid platform-specific code.

– Sven Marnach
Nov 26 '18 at 10:22


















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53477846%2fstart-another-program-then-quit%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Wiesbaden

Marschland

Dieringhausen