run_external: remove inner quotes once nushell gets = sign (#13073)

# Description
Fixes: #13066

nushell should remove argument values' inner quote once it gets `=`.
Whatever it's a flag or not, and it also replace from `\"` to `"` before
passing it to external commands.

# User-Facing Changes
Given the shell script:
```shell
# test.sh
echo $@
```
## Before
```
>  sh test.sh -ldflags="-s -w" github.com
-ldflags="-s -w" github.com
> sh test.sh exp='-s -w' github.com
exp='-s -w' github.com
```
## After
```
>  sh test.sh -ldflags="-s -w" github.com
-ldflags=-s -w github.com
> sh test.sh exp='-s -w' github.com
exp=-s -w github.com
```

# Tests + Formatting
Added some tests
This commit is contained in:
Wind 2024-06-06 11:03:34 +08:00 committed by GitHub
parent 75d5807dcd
commit 5d163c1bcc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 33 additions and 8 deletions

View file

@ -218,14 +218,16 @@ impl Command for External {
/// Removes surrounding quotes from a string. Doesn't remove quotes from raw /// Removes surrounding quotes from a string. Doesn't remove quotes from raw
/// strings. Returns the original string if it doesn't have matching quotes. /// strings. Returns the original string if it doesn't have matching quotes.
fn remove_quotes(s: &str) -> &str { fn remove_quotes(s: &str) -> Cow<'_, str> {
let quoted_by_double_quotes = s.len() >= 2 && s.starts_with('"') && s.ends_with('"'); let quoted_by_double_quotes = s.len() >= 2 && s.starts_with('"') && s.ends_with('"');
let quoted_by_single_quotes = s.len() >= 2 && s.starts_with('\'') && s.ends_with('\''); let quoted_by_single_quotes = s.len() >= 2 && s.starts_with('\'') && s.ends_with('\'');
let quoted_by_backticks = s.len() >= 2 && s.starts_with('`') && s.ends_with('`'); let quoted_by_backticks = s.len() >= 2 && s.starts_with('`') && s.ends_with('`');
if quoted_by_double_quotes || quoted_by_single_quotes || quoted_by_backticks { if quoted_by_double_quotes {
&s[1..s.len() - 1] Cow::Owned(s[1..s.len() - 1].to_string().replace(r#"\""#, "\""))
} else if quoted_by_single_quotes || quoted_by_backticks {
Cow::Borrowed(&s[1..s.len() - 1])
} else { } else {
s Cow::Borrowed(s)
} }
} }
@ -400,10 +402,6 @@ fn expand_glob(
/// with double quotes, single quotes, or backticks. Only removes the outermost /// with double quotes, single quotes, or backticks. Only removes the outermost
/// pair of quotes after the equal sign. /// pair of quotes after the equal sign.
fn remove_inner_quotes(arg: &str) -> Cow<'_, str> { fn remove_inner_quotes(arg: &str) -> Cow<'_, str> {
// Check that `arg` is a long option.
if !arg.starts_with("--") {
return Cow::Borrowed(arg);
}
// Split `arg` on the first `=`. // Split `arg` on the first `=`.
let Some((option, value)) = arg.split_once('=') else { let Some((option, value)) = arg.split_once('=') else {
return Cow::Borrowed(arg); return Cow::Borrowed(arg);
@ -660,6 +658,7 @@ mod test {
assert_eq!(remove_quotes(r#"`foo '"' bar`"#), r#"foo '"' bar"#); assert_eq!(remove_quotes(r#"`foo '"' bar`"#), r#"foo '"' bar"#);
assert_eq!(remove_quotes(r#"'foo' bar"#), r#"'foo' bar"#); assert_eq!(remove_quotes(r#"'foo' bar"#), r#"'foo' bar"#);
assert_eq!(remove_quotes(r#"r#'foo'#"#), r#"r#'foo'#"#); assert_eq!(remove_quotes(r#"r#'foo'#"#), r#"r#'foo'#"#);
assert_eq!(remove_quotes(r#""foo\" bar""#), r#"foo" bar"#);
} }
#[test] #[test]
@ -758,6 +757,18 @@ mod test {
let actual = remove_inner_quotes(r#"--option "value""#); let actual = remove_inner_quotes(r#"--option "value""#);
let expected = r#"--option "value""#; let expected = r#"--option "value""#;
assert_eq!(actual, expected); assert_eq!(actual, expected);
let actual = remove_inner_quotes(r#"-option="value""#);
let expected = r#"-option=value"#;
assert_eq!(actual, expected);
let actual = remove_inner_quotes(r#"option="value""#);
let expected = r#"option=value"#;
assert_eq!(actual, expected);
let actual = remove_inner_quotes(r#"option="v\"value""#);
let expected = r#"option=v"value"#;
assert_eq!(actual, expected);
} }
#[test] #[test]

View file

@ -590,4 +590,18 @@ mod external_command_arguments {
assert_eq!(actual.out, "a;&$(hello)"); assert_eq!(actual.out, "a;&$(hello)");
} }
#[test]
fn remove_quotes_in_shell_arguments() {
let actual = nu!("nu --testbin cococo expression='-r -w'");
assert_eq!(actual.out, "expression=-r -w");
let actual = nu!(r#"nu --testbin cococo expression="-r -w""#);
assert_eq!(actual.out, "expression=-r -w");
let actual = nu!("nu --testbin cococo expression='-r -w'");
assert_eq!(actual.out, "expression=-r -w");
let actual = nu!(r#"nu --testbin cococo expression="-r\" -w""#);
assert_eq!(actual.out, r#"expression=-r" -w"#);
let actual = nu!(r#"nu --testbin cococo expression='-r\" -w'"#);
assert_eq!(actual.out, r#"expression=-r\" -w"#);
}
} }