diff --git a/crates/nu-command/tests/commands/run_external.rs b/crates/nu-command/tests/commands/run_external.rs index 3dac3c3c81..d92573426e 100644 --- a/crates/nu-command/tests/commands/run_external.rs +++ b/crates/nu-command/tests/commands/run_external.rs @@ -186,6 +186,20 @@ fn external_arg_with_variable_name() { }) } +#[test] +fn external_command_escape_args() { + Playground::setup("external failed command with semicolon", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ^echo "\"abcd" + "# + )); + + assert_eq!(actual.out, r#""abcd"#); + }) +} + #[cfg(windows)] #[test] fn explicit_glob_windows() { diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 33e5f6e7a5..877c54ae2d 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -331,7 +331,13 @@ pub fn parse_external_call( args.push(arg); } else { // Eval stage trims the quotes, so we don't have to do the same thing when parsing. - let contents = String::from_utf8_lossy(contents).to_string(); + let contents = if contents.starts_with(b"\"") { + let (contents, err) = unescape_string(contents, *span); + error = error.or(err); + String::from_utf8_lossy(&contents).to_string() + } else { + String::from_utf8_lossy(contents).to_string() + }; args.push(Expression { expr: Expr::String(contents),