forked from extern/nushell
keep raw for variable inputed argument (#6426)
* keep raw for variable inputed argument * fix clippy for windows * make test runs on windows
This commit is contained in:
parent
34d7c17e78
commit
b88ace4cde
@ -70,6 +70,7 @@ impl Command for ConfigEnv {
|
|||||||
let command = ExternalCommand {
|
let command = ExternalCommand {
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
|
arg_keep_raw: vec![false],
|
||||||
redirect_stdout: false,
|
redirect_stdout: false,
|
||||||
redirect_stderr: false,
|
redirect_stderr: false,
|
||||||
env_vars: env_vars_str,
|
env_vars: env_vars_str,
|
||||||
|
@ -70,6 +70,7 @@ impl Command for ConfigNu {
|
|||||||
let command = ExternalCommand {
|
let command = ExternalCommand {
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
|
arg_keep_raw: vec![false],
|
||||||
redirect_stdout: false,
|
redirect_stdout: false,
|
||||||
redirect_stderr: false,
|
redirect_stderr: false,
|
||||||
env_vars: env_vars_str,
|
env_vars: env_vars_str,
|
||||||
|
@ -65,15 +65,27 @@ fn exec(
|
|||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
use std::os::unix::process::CommandExt;
|
use std::os::unix::process::CommandExt;
|
||||||
|
|
||||||
use nu_engine::{current_dir, env_to_strings, CallExt};
|
|
||||||
use nu_protocol::Spanned;
|
|
||||||
|
|
||||||
use super::run_external::ExternalCommand;
|
use super::run_external::ExternalCommand;
|
||||||
|
use nu_engine::{current_dir, env_to_strings, CallExt};
|
||||||
|
use nu_protocol::ast::Expr;
|
||||||
|
use nu_protocol::Spanned;
|
||||||
|
|
||||||
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let name_span = name.span;
|
let name_span = name.span;
|
||||||
|
|
||||||
let args: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
let args: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||||
|
let args_expr: Vec<nu_protocol::ast::Expression> =
|
||||||
|
call.positional_iter().skip(1).cloned().collect();
|
||||||
|
let mut arg_keep_raw = vec![];
|
||||||
|
for one_arg_expr in args_expr {
|
||||||
|
match one_arg_expr.expr {
|
||||||
|
// refer to `parse_dollar_expr` function
|
||||||
|
// the expression type of $variable_name, $"($variable_name)"
|
||||||
|
// will be Expr::StringInterpolation, Expr::FullCellPath
|
||||||
|
Expr::StringInterpolation(_) | Expr::FullCellPath(_) => arg_keep_raw.push(true),
|
||||||
|
_ => arg_keep_raw.push(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let env_vars = env_to_strings(engine_state, stack)?;
|
let env_vars = env_to_strings(engine_state, stack)?;
|
||||||
@ -82,6 +94,7 @@ fn exec(
|
|||||||
let external_command = ExternalCommand {
|
let external_command = ExternalCommand {
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
|
arg_keep_raw,
|
||||||
env_vars,
|
env_vars,
|
||||||
redirect_stdout: true,
|
redirect_stdout: true,
|
||||||
redirect_stderr: false,
|
redirect_stderr: false,
|
||||||
|
@ -2,6 +2,7 @@ use fancy_regex::Regex;
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nu_engine::env_to_strings;
|
use nu_engine::env_to_strings;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::ast::{Expr, Expression};
|
||||||
use nu_protocol::did_you_mean;
|
use nu_protocol::did_you_mean;
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
||||||
@ -70,23 +71,40 @@ impl Command for External {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut spanned_args = vec![];
|
let mut spanned_args = vec![];
|
||||||
for one_arg in args {
|
let args_expr: Vec<Expression> = call.positional_iter().skip(1).cloned().collect();
|
||||||
|
let mut arg_keep_raw = vec![];
|
||||||
|
for (one_arg, one_arg_expr) in args.into_iter().zip(args_expr) {
|
||||||
match one_arg {
|
match one_arg {
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
// turn all the strings in the array into params.
|
// turn all the strings in the array into params.
|
||||||
// Example: one_arg may be something like ["ls" "-a"]
|
// Example: one_arg may be something like ["ls" "-a"]
|
||||||
// convert it to "ls" "-a"
|
// convert it to "ls" "-a"
|
||||||
for v in vals {
|
for v in vals {
|
||||||
spanned_args.push(value_as_spanned(v)?)
|
spanned_args.push(value_as_spanned(v)?);
|
||||||
|
// for arguments in list, it's always treated as a whole arguments
|
||||||
|
arg_keep_raw.push(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val => spanned_args.push(value_as_spanned(val)?),
|
val => {
|
||||||
|
spanned_args.push(value_as_spanned(val)?);
|
||||||
|
match one_arg_expr.expr {
|
||||||
|
// refer to `parse_dollar_expr` function
|
||||||
|
// the expression type of $variable_name, $"($variable_name)"
|
||||||
|
// will be Expr::StringInterpolation, Expr::FullCellPath
|
||||||
|
Expr::StringInterpolation(_) | Expr::FullCellPath(_) => {
|
||||||
|
arg_keep_raw.push(true)
|
||||||
|
}
|
||||||
|
_ => arg_keep_raw.push(false),
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let command = ExternalCommand {
|
let command = ExternalCommand {
|
||||||
name,
|
name,
|
||||||
args: spanned_args,
|
args: spanned_args,
|
||||||
|
arg_keep_raw,
|
||||||
redirect_stdout,
|
redirect_stdout,
|
||||||
redirect_stderr,
|
redirect_stderr,
|
||||||
env_vars: env_vars_str,
|
env_vars: env_vars_str,
|
||||||
@ -107,6 +125,7 @@ impl Command for External {
|
|||||||
pub struct ExternalCommand {
|
pub struct ExternalCommand {
|
||||||
pub name: Spanned<String>,
|
pub name: Spanned<String>,
|
||||||
pub args: Vec<Spanned<String>>,
|
pub args: Vec<Spanned<String>>,
|
||||||
|
pub arg_keep_raw: Vec<bool>,
|
||||||
pub redirect_stdout: bool,
|
pub redirect_stdout: bool,
|
||||||
pub redirect_stderr: bool,
|
pub redirect_stderr: bool,
|
||||||
pub env_vars: HashMap<String, String>,
|
pub env_vars: HashMap<String, String>,
|
||||||
@ -508,12 +527,17 @@ impl ExternalCommand {
|
|||||||
|
|
||||||
let mut process = std::process::Command::new(&head);
|
let mut process = std::process::Command::new(&head);
|
||||||
|
|
||||||
for arg in self.args.iter() {
|
for (arg, arg_keep_raw) in self.args.iter().zip(self.arg_keep_raw.iter()) {
|
||||||
// if arg is quoted, like "aa", 'aa', `aa`.
|
// if arg is quoted, like "aa", 'aa', `aa`, or:
|
||||||
|
// if arg is a variable or String interpolation, like: $variable_name, $"($variable_name)"
|
||||||
// `as_a_whole` will be true, so nu won't remove the inner quotes.
|
// `as_a_whole` will be true, so nu won't remove the inner quotes.
|
||||||
let (trimmed_args, run_glob_expansion, as_a_whole) = trim_enclosing_quotes(&arg.item);
|
let (trimmed_args, run_glob_expansion, mut keep_raw) = trim_enclosing_quotes(&arg.item);
|
||||||
|
if *arg_keep_raw {
|
||||||
|
keep_raw = true;
|
||||||
|
}
|
||||||
|
|
||||||
let mut arg = Spanned {
|
let mut arg = Spanned {
|
||||||
item: if as_a_whole {
|
item: if keep_raw {
|
||||||
trimmed_args
|
trimmed_args
|
||||||
} else {
|
} else {
|
||||||
remove_quotes(trimmed_args)
|
remove_quotes(trimmed_args)
|
||||||
@ -643,7 +667,7 @@ fn shell_arg_escape(arg: &str) -> String {
|
|||||||
/// This function returns a tuple with 3 items:
|
/// This function returns a tuple with 3 items:
|
||||||
/// 1st item: trimmed string.
|
/// 1st item: trimmed string.
|
||||||
/// 2nd item: a boolean value indicate if it's ok to run glob expansion.
|
/// 2nd item: a boolean value indicate if it's ok to run glob expansion.
|
||||||
/// 3rd item: a boolean value indicate if we need to make input as a whole.
|
/// 3rd item: a boolean value indicate if we need to keep raw string.
|
||||||
fn trim_enclosing_quotes(input: &str) -> (String, bool, bool) {
|
fn trim_enclosing_quotes(input: &str) -> (String, bool, bool) {
|
||||||
let mut chars = input.chars();
|
let mut chars = input.chars();
|
||||||
|
|
||||||
|
@ -168,6 +168,24 @@ fn external_arg_with_long_flag_value_quoted() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_arg_with_variable_name() {
|
||||||
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(), pipeline(
|
||||||
|
r#"
|
||||||
|
let dump_command = "PGPASSWORD='db_secret' pg_dump -Fc -h 'db.host' -p '$db.port' -U postgres -d 'db_name' > '/tmp/dump_name'";
|
||||||
|
nu --testbin nonu $dump_command
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
actual.out,
|
||||||
|
r#"PGPASSWORD='db_secret' pg_dump -Fc -h 'db.host' -p '$db.port' -U postgres -d 'db_name' > '/tmp/dump_name'"#
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
#[test]
|
#[test]
|
||||||
fn explicit_glob_windows() {
|
fn explicit_glob_windows() {
|
||||||
|
Loading…
Reference in New Issue
Block a user