mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00: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
2 changed files with 79 additions and 7 deletions
|
@ -5,6 +5,8 @@ use nu_protocol::{did_you_mean, process::ChildProcess, ByteStream, NuGlob, OutDe
|
||||||
use nu_system::ForegroundChild;
|
use nu_system::ForegroundChild;
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use pathdiff::diff_paths;
|
use pathdiff::diff_paths;
|
||||||
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
ffi::{OsStr, OsString},
|
ffi::{OsStr, OsString},
|
||||||
|
@ -91,6 +93,22 @@ impl Command for External {
|
||||||
false
|
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
|
// Find the absolute path to the executable. On Windows, set the
|
||||||
// executable to "cmd.exe" if it's a CMD internal command. If the
|
// executable to "cmd.exe" if it's a CMD internal command. If the
|
||||||
// command is not found, display a helpful error message.
|
// 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)
|
&& (is_cmd_internal_command(&name_str) || potential_nuscript_in_windows)
|
||||||
{
|
{
|
||||||
PathBuf::from("cmd.exe")
|
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 {
|
} else {
|
||||||
// Determine the PATH to be used and then use `which` to find it - though this has no
|
// 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
|
// effect if it's an absolute path already
|
||||||
let paths = nu_engine::env::path_str(engine_state, stack, call.head)?;
|
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));
|
return Err(command_not_found(&name_str, call.head, engine_state, stack));
|
||||||
};
|
};
|
||||||
executable
|
executable
|
||||||
|
@ -123,15 +146,29 @@ impl Command for External {
|
||||||
let args = eval_arguments_from_call(engine_state, stack, call)?;
|
let args = eval_arguments_from_call(engine_state, stack, call)?;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if is_cmd_internal_command(&name_str) || potential_nuscript_in_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 /D flag disables execution of AutoRun commands from registry.
|
||||||
// The /C flag followed by a command name instructs CMD to execute
|
// The /C flag followed by a command name instructs CMD to execute
|
||||||
// that command and quit.
|
// that command and quit.
|
||||||
command.args(["/D", "/C", &name_str]);
|
command.args(["/D", "/C", &expanded_name.to_string_lossy()]);
|
||||||
for arg in &args {
|
for arg in &args {
|
||||||
command.raw_arg(escape_cmd_argument(arg)?);
|
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 {
|
} else {
|
||||||
command.args(args.into_iter().map(|s| s.item));
|
command.args(args.into_iter().map(|s| s.item));
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,9 +355,9 @@ fn external_command_receives_raw_binary_data() {
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[test]
|
#[test]
|
||||||
fn can_run_batch_files() {
|
fn can_run_cmd_files() {
|
||||||
use nu_test_support::fs::Stub::FileWithContent;
|
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(
|
sandbox.with_files(&[FileWithContent(
|
||||||
"foo.cmd",
|
"foo.cmd",
|
||||||
r#"
|
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)]
|
#[cfg(windows)]
|
||||||
#[test]
|
#[test]
|
||||||
fn can_run_batch_files_without_cmd_extension() {
|
fn can_run_batch_files_without_cmd_extension() {
|
||||||
use nu_test_support::fs::Stub::FileWithContent;
|
use nu_test_support::fs::Stub::FileWithContent;
|
||||||
Playground::setup(
|
Playground::setup(
|
||||||
"run a Windows batch file without specifying the extension",
|
"run a Windows cmd file without specifying the extension",
|
||||||
|dirs, sandbox| {
|
|dirs, sandbox| {
|
||||||
sandbox.with_files(&[FileWithContent(
|
sandbox.with_files(&[FileWithContent(
|
||||||
"foo.cmd",
|
"foo.cmd",
|
||||||
|
@ -440,3 +458,20 @@ fn redirect_combine() {
|
||||||
assert_eq!(actual.out, "FooBar");
|
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 a new issue