Add exec command for Windows (#11001)

# Description
Based of the work and discussion in #10844, this PR adds the `exec`
command for Windows. This is done by simply spawning a
`std::process::Command` and then immediately exiting via
`std::process::exit` once the child process is finished. The child
process's exit code is passed to `exit`.

# User-Facing Changes
The `exec` command is now available on Windows, and there should be no
change in behaviour for Unix systems.
This commit is contained in:
Ian Manske 2023-11-08 20:50:25 +00:00 committed by GitHub
parent 59ea28cf06
commit 1fd3bc1ba6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 33 deletions

View File

@ -115,6 +115,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
bind_command! {
Complete,
External,
Exec,
NuCheck,
Sys,
};
@ -145,9 +146,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
ViewSpan,
};
#[cfg(unix)]
bind_command! { Exec }
#[cfg(windows)]
bind_command! { RegistryQuery }

View File

@ -1,11 +1,10 @@
use super::run_external::create_external_command;
use nu_engine::{current_dir, CallExt};
use nu_engine::current_dir;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type,
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type,
};
use std::os::unix::process::CommandExt;
#[derive(Clone)]
pub struct Exec;
@ -24,11 +23,12 @@ impl Command for Exec {
}
fn usage(&self) -> &str {
"Execute a command, replacing the current process."
"Execute a command, replacing or exiting the current process, depending on platform."
}
fn extra_usage(&self) -> &str {
"Currently supported only on Unix-based systems."
r#"On Unix-based systems, the current process is replaced with the command.
On Windows based systems, Nushell will wait for the command to finish and then exit with the command's exit code."#
}
fn run(
@ -62,36 +62,49 @@ fn exec(
stack: &mut Stack,
call: &Call,
) -> Result<PipelineData, ShellError> {
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
let name_span = name.span;
let redirect_stdout = call.has_flag("redirect-stdout");
let redirect_stderr = call.has_flag("redirect-stderr");
let redirect_combine = call.has_flag("redirect-combine");
let trim_end_newline = call.has_flag("trim-end-newline");
let external_command = create_external_command(
engine_state,
stack,
call,
redirect_stdout,
redirect_stderr,
redirect_combine,
trim_end_newline,
)?;
let external_command =
create_external_command(engine_state, stack, call, false, false, false, false)?;
let cwd = current_dir(engine_state, stack)?;
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
command.current_dir(cwd);
command.envs(&external_command.env_vars);
command.envs(external_command.env_vars);
let err = command.exec(); // this replaces our process, should not return
// this either replaces our process and should not return,
// or the exec fails and we get an error back
exec_impl(command, call.head)
}
#[cfg(unix)]
fn exec_impl(mut command: std::process::Command, span: Span) -> Result<PipelineData, ShellError> {
use std::os::unix::process::CommandExt;
let error = command.exec();
Err(ShellError::GenericError(
"Error on exec".to_string(),
err.to_string(),
Some(name_span),
"Error on exec".into(),
error.to_string(),
Some(span),
None,
Vec::new(),
))
}
#[cfg(windows)]
fn exec_impl(mut command: std::process::Command, span: Span) -> Result<PipelineData, ShellError> {
match command.spawn() {
Ok(mut child) => match child.wait() {
Ok(status) => std::process::exit(status.code().unwrap_or(0)),
Err(e) => Err(ShellError::ExternalCommand {
label: "Error in external command".into(),
help: e.to_string(),
span,
}),
},
Err(e) => Err(ShellError::ExternalCommand {
label: "Error spawning external command".into(),
help: e.to_string(),
span,
}),
}
}

View File

@ -1,5 +1,4 @@
mod complete;
#[cfg(unix)]
mod exec;
mod nu_check;
#[cfg(any(
@ -16,7 +15,6 @@ mod sys;
mod which_;
pub use complete::Complete;
#[cfg(unix)]
pub use exec::Exec;
pub use nu_check::NuCheck;
#[cfg(any(

View File

@ -24,7 +24,6 @@ mod echo;
mod empty;
mod error_make;
mod every;
#[cfg(not(windows))]
mod exec;
mod export_def;
mod fill;