Skip to content

Commit

Permalink
support glob value expand
Browse files Browse the repository at this point in the history
  • Loading branch information
WindSoilder committed Feb 24, 2024
1 parent 9f95bdd commit 4a22bdf
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 17 deletions.
2 changes: 1 addition & 1 deletion crates/nu-command/src/env/config/utils.rs
Expand Up @@ -31,7 +31,7 @@ pub(crate) fn gen_command(
ExternalCommand {
name,
args,
arg_keep_raw: vec![false; number_of_args],
arg_constraints: vec![(false, false); number_of_args],
redirect_stdout: false,
redirect_stderr: false,
redirect_combine: false,
Expand Down
44 changes: 28 additions & 16 deletions crates/nu-command/src/system/run_external.rs
Expand Up @@ -104,6 +104,16 @@ impl Command for External {
}
}

fn is_glob_val(v: &Value) -> bool {
matches!(
v,
Value::Glob {
no_expand: false,
..
}
)
}

/// Creates ExternalCommand from a call
pub fn create_external_command(
engine_state: &EngineState,
Expand Down Expand Up @@ -133,7 +143,7 @@ pub fn create_external_command(
}

let mut spanned_args = vec![];
let mut arg_keep_raw = vec![];
let mut arg_keep_raw_and_expand_glob = vec![];
for (arg, spread) in call.rest_iter(1) {
match eval_expression(engine_state, stack, arg)? {
Value::List { vals, .. } => {
Expand All @@ -142,9 +152,9 @@ pub fn create_external_command(
// Example: one_arg may be something like ["ls" "-a"]
// convert it to "ls" "-a"
for v in vals {
spanned_args.push(value_as_spanned(v)?);
// for arguments in list, it's always treated as a whole arguments
arg_keep_raw.push(true);
arg_keep_raw_and_expand_glob.push((true, is_glob_val(&v)));
spanned_args.push(value_as_spanned(v)?);
}
} else {
return Err(ShellError::CannotPassListToExternal {
Expand All @@ -158,16 +168,16 @@ pub fn create_external_command(
if spread {
return Err(ShellError::CannotSpreadAsList { span: arg.span });
} else {
spanned_args.push(value_as_spanned(val)?);
match arg.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_and_expand_glob.push((true, is_glob_val(&val)))
}
_ => arg_keep_raw.push(false),
_ => arg_keep_raw_and_expand_glob.push((false, false)),
}
spanned_args.push(value_as_spanned(val)?);
}
}
}
Expand All @@ -176,7 +186,7 @@ pub fn create_external_command(
Ok(ExternalCommand {
name,
args: spanned_args,
arg_keep_raw,
arg_constraints: arg_keep_raw_and_expand_glob,
redirect_stdout,
redirect_stderr,
redirect_combine,
Expand All @@ -189,7 +199,7 @@ pub fn create_external_command(
pub struct ExternalCommand {
pub name: Spanned<String>,
pub args: Vec<Spanned<String>>,
pub arg_keep_raw: Vec<bool>,
pub arg_constraints: Vec<(bool, bool)>,
pub redirect_stdout: bool,
pub redirect_stderr: bool,
pub redirect_combine: bool,
Expand Down Expand Up @@ -669,8 +679,9 @@ impl ExternalCommand {

let mut process = std::process::Command::new(head);

for (arg, arg_keep_raw) in self.args.iter().zip(self.arg_keep_raw.iter()) {
trim_expand_and_apply_arg(&mut process, arg, arg_keep_raw, cwd);
for (arg, (arg_keep_raw, is_glob_val)) in self.args.iter().zip(self.arg_constraints.iter())
{
trim_expand_and_apply_arg(&mut process, arg, *arg_keep_raw, *is_glob_val, cwd);
}

Ok(process)
Expand All @@ -687,15 +698,16 @@ impl ExternalCommand {

process.arg("/c");
process.arg(&self.name.item);
for (arg, arg_keep_raw) in self.args.iter().zip(self.arg_keep_raw.iter()) {
for (arg, (arg_keep_raw, is_glob_val)) in self.args.iter().zip(self.arg_constraints.iter())
{
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
// cmd.exe needs to have a caret to escape a pipe
let arg = Spanned {
item: arg.item.replace('|', "^|"),
span: arg.span,
};

trim_expand_and_apply_arg(&mut process, &arg, arg_keep_raw, cwd)
trim_expand_and_apply_arg(&mut process, &arg, *arg_keep_raw, *is_glob_val, cwd)
}

process
Expand All @@ -705,17 +717,17 @@ impl ExternalCommand {
fn trim_expand_and_apply_arg(
process: &mut CommandSys,
arg: &Spanned<String>,
arg_keep_raw: &bool,
arg_keep_raw: bool,
is_glob_val: bool,
cwd: &str,
) {
// 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.
let (trimmed_args, mut run_glob_expansion, mut keep_raw) = trim_enclosing_quotes(&arg.item);
if *arg_keep_raw {
if arg_keep_raw {
keep_raw = true;
// it's a list or a variable, don't run glob expansion either
run_glob_expansion = false;
run_glob_expansion = is_glob_val;
}
let mut arg = Spanned {
item: if keep_raw {
Expand Down
1 change: 1 addition & 0 deletions crates/nu-protocol/src/value/mod.rs
Expand Up @@ -394,6 +394,7 @@ impl Value {
Value::Int { val, .. } => Ok(Cow::Owned(val.to_string())),
Value::Float { val, .. } => Ok(Cow::Owned(val.to_string())),
Value::String { val, .. } => Ok(Cow::Borrowed(val)),
Value::Glob { val, .. } => Ok(Cow::Borrowed(val)),
Value::Binary { val, .. } => match std::str::from_utf8(val) {
Ok(s) => Ok(Cow::Borrowed(s)),
Err(_) => self.cant_convert_to("string"),
Expand Down
14 changes: 14 additions & 0 deletions tests/shell/pipeline/commands/external.rs
Expand Up @@ -197,6 +197,20 @@ fn run_glob_if_pass_variable_to_external() {
})
}

#[test]
fn run_glob_if_pass_glob_variable_to_external() {
Playground::setup("run_glob_on_external", |dirs, sandbox| {
sandbox.with_files(vec![
EmptyFile("jt_likes_cake.txt"),
EmptyFile("andres_likes_arepas.txt"),
]);

let actual = nu!(cwd: dirs.test(), r#"let f: glob = "*.txt"; nu --testbin nonu $f"#);

assert!(actual.out.contains("jt_likes_cake.txt"));
assert!(actual.out.contains("andres_likes_arepas.txt"));
})
}
mod it_evaluation {
use super::nu;
use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed};
Expand Down

0 comments on commit 4a22bdf

Please sign in to comment.