mirror of
https://github.com/nushell/nushell.git
synced 2025-05-28 14:07:08 +02: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.  or once it's in json, just consume it with nushell.  # 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…
x
Reference in New Issue
Block a user