From 8c2b1a22d4bbee0b1312c232a5520ea01b7124f8 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 14 May 2025 13:21:02 -0500 Subject: [PATCH] allow powershell scripts in the path to be executed (#15760) # Description This PR fixes a bug where powershell scripts were only allowed to be executed if they were in the directory that you executed them from. This fix allows the scripts to be anywhere in the path. closes #15759 # User-Facing Changes # Tests + Formatting # After Submitting --- crates/nu-command/src/system/run_external.rs | 71 +++++++++----------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 526c5ccdd3..40b79a2805 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -110,46 +110,47 @@ impl Command for External { }; // let's make sure it's a .ps1 script, but only on Windows - let potential_powershell_script = if cfg!(windows) { - if let Some(executable) = which(&expanded_name, "", cwd.as_ref()) { + let (potential_powershell_script, path_to_ps1_executable) = if cfg!(windows) { + if let Some(executable) = which(&expanded_name, &paths, cwd.as_ref()) { let ext = executable .extension() .unwrap_or_default() .to_string_lossy() .to_uppercase(); - ext == "PS1" + (ext == "PS1", Some(executable)) } else { - false + (false, None) } } else { - false + (false, None) }; // Find the absolute path to the executable. On Windows, set the // executable to "cmd.exe" if it's a CMD internal command. If the // command is not found, display a helpful error message. - let executable = - if cfg!(windows) && (is_cmd_internal_command(&name_str) || pathext_script_in_windows) { - PathBuf::from("cmd.exe") - } else if cfg!(windows) && potential_powershell_script { - // If we're on Windows and we're trying to run a PowerShell script, we'll use - // `powershell.exe` to run it. We shouldn't have to check for powershell.exe because - // it's automatically installed on all modern windows systems. - PathBuf::from("powershell.exe") - } else { - // Determine the PATH to be used and then use `which` to find it - though this has no - // effect if it's an absolute path already - let Some(executable) = which(&expanded_name, &paths, cwd.as_ref()) else { - return Err(command_not_found( - &name_str, - call.head, - engine_state, - stack, - &cwd, - )); - }; - executable + let executable = if cfg!(windows) + && (is_cmd_internal_command(&name_str) || pathext_script_in_windows) + { + PathBuf::from("cmd.exe") + } else if cfg!(windows) && potential_powershell_script && path_to_ps1_executable.is_some() { + // If we're on Windows and we're trying to run a PowerShell script, we'll use + // `powershell.exe` to run it. We shouldn't have to check for powershell.exe because + // it's automatically installed on all modern windows systems. + PathBuf::from("powershell.exe") + } else { + // Determine the PATH to be used and then use `which` to find it - though this has no + // effect if it's an absolute path already + let Some(executable) = which(&expanded_name, &paths, cwd.as_ref()) else { + return Err(command_not_found( + &name_str, + call.head, + engine_state, + stack, + &cwd, + )); }; + executable + }; // Create the command. let mut command = std::process::Command::new(&executable); @@ -174,20 +175,10 @@ impl Command for External { command.raw_arg(escape_cmd_argument(arg)?); } } else if potential_powershell_script { - use nu_path::canonicalize_with; - - // canonicalize the path to the script so that tests pass - let canon_path = if let Ok(cwd) = engine_state.cwd_as_string(None) { - canonicalize_with(&expanded_name, cwd).map_err(|err| { - IoError::new(err.kind(), call.head, PathBuf::from(&expanded_name)) - })? - } else { - // If we can't get the current working directory, just provide the expanded name - expanded_name - }; - // The -Command flag followed by a script name instructs PowerShell to - // execute that script and quit. - command.args(["-Command", &canon_path.to_string_lossy()]); + command.args([ + "-Command", + &path_to_ps1_executable.unwrap_or_default().to_string_lossy(), + ]); for arg in &args { command.raw_arg(arg.item.clone()); }