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:
WindSoilder 2022-08-27 21:22:02 +08:00 committed by GitHub
parent 34d7c17e78
commit b88ace4cde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 68 additions and 11 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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();

View File

@ -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() {