mirror of
https://github.com/nushell/nushell.git
synced 2024-11-24 09:23:38 +01:00
allow ps1 files to be executed without pwsh/powershell -c file.ps1 (#14379)
# Description This PR allows nushell to run powershell scripts easier. You can already do `powershell -c script.ps1` but this PR takes it a step further by doing the `powershell -c` part for you. So, if you have script.ps1 you can execute it by running it in the command position of the repl. ![image](https://github.com/user-attachments/assets/0661a746-27d9-4d21-b576-c244ff7fab2b) or once it's in json, just consume it with nushell. ![image](https://github.com/user-attachments/assets/38f5c5d8-3659-41f0-872b-91a14909760b) # User-Facing Changes Easier to run powershell scripts. It should work on Windows with powershell.exe. # Tests + Formatting Added 1 test # After Submitting --------- Co-authored-by: Wind <WindSoilder@outlook.com>
This commit is contained in:
parent
5d1eb031eb
commit
42d2adc3e0
@ -5,6 +5,8 @@ use nu_protocol::{did_you_mean, process::ChildProcess, ByteStream, NuGlob, OutDe
|
||||
use nu_system::ForegroundChild;
|
||||
use nu_utils::IgnoreCaseExt;
|
||||
use pathdiff::diff_paths;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ffi::{OsStr, OsString},
|
||||
@ -91,6 +93,22 @@ impl Command for External {
|
||||
false
|
||||
};
|
||||
|
||||
// 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 ext = executable
|
||||
.extension()
|
||||
.unwrap_or_default()
|
||||
.to_string_lossy()
|
||||
.to_uppercase();
|
||||
ext == "PS1"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// 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.
|
||||
@ -98,11 +116,16 @@ impl Command for External {
|
||||
&& (is_cmd_internal_command(&name_str) || potential_nuscript_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 paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
|
||||
let Some(executable) = which(expanded_name, &paths, cwd.as_ref()) else {
|
||||
let Some(executable) = which(&expanded_name, &paths, cwd.as_ref()) else {
|
||||
return Err(command_not_found(&name_str, call.head, engine_state, stack));
|
||||
};
|
||||
executable
|
||||
@ -123,15 +146,29 @@ impl Command for External {
|
||||
let args = eval_arguments_from_call(engine_state, stack, call)?;
|
||||
#[cfg(windows)]
|
||||
if is_cmd_internal_command(&name_str) || potential_nuscript_in_windows {
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
// The /D flag disables execution of AutoRun commands from registry.
|
||||
// The /C flag followed by a command name instructs CMD to execute
|
||||
// that command and quit.
|
||||
command.args(["/D", "/C", &name_str]);
|
||||
command.args(["/D", "/C", &expanded_name.to_string_lossy()]);
|
||||
for arg in &args {
|
||||
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)?
|
||||
} 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()]);
|
||||
for arg in &args {
|
||||
command.raw_arg(arg.item.clone());
|
||||
}
|
||||
} else {
|
||||
command.args(args.into_iter().map(|s| s.item));
|
||||
}
|
||||
|
@ -355,9 +355,9 @@ fn external_command_receives_raw_binary_data() {
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn can_run_batch_files() {
|
||||
fn can_run_cmd_files() {
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
Playground::setup("run a Windows batch file", |dirs, sandbox| {
|
||||
Playground::setup("run a Windows cmd file", |dirs, sandbox| {
|
||||
sandbox.with_files(&[FileWithContent(
|
||||
"foo.cmd",
|
||||
r#"
|
||||
@ -371,12 +371,30 @@ fn can_run_batch_files() {
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn can_run_batch_files() {
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
Playground::setup("run a Windows batch file", |dirs, sandbox| {
|
||||
sandbox.with_files(&[FileWithContent(
|
||||
"foo.bat",
|
||||
r#"
|
||||
@echo off
|
||||
echo Hello World
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(cwd: dirs.test(), pipeline("foo.bat"));
|
||||
assert!(actual.out.contains("Hello World"));
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn can_run_batch_files_without_cmd_extension() {
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
Playground::setup(
|
||||
"run a Windows batch file without specifying the extension",
|
||||
"run a Windows cmd file without specifying the extension",
|
||||
|dirs, sandbox| {
|
||||
sandbox.with_files(&[FileWithContent(
|
||||
"foo.cmd",
|
||||
@ -440,3 +458,20 @@ fn redirect_combine() {
|
||||
assert_eq!(actual.out, "FooBar");
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn can_run_ps1_files() {
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
Playground::setup("run_a_windows_ps_file", |dirs, sandbox| {
|
||||
sandbox.with_files(&[FileWithContent(
|
||||
"foo.ps1",
|
||||
r#"
|
||||
Write-Host Hello World
|
||||
"#,
|
||||
)]);
|
||||
|
||||
let actual = nu!(cwd: dirs.test(), pipeline("foo.ps1"));
|
||||
assert!(actual.out.contains("Hello World"));
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user