1
0
mirror of https://github.com/nushell/nushell.git synced 2025-07-13 12:56:22 +02:00
Files
.cargo
.githooks
.github
assets
benches
crates
nu-cli
nu-cmd-base
nu-cmd-extra
nu-cmd-lang
nu-cmd-plugin
nu-color-config
nu-command
src
tests
commands
assignment
bytes
conversions
database
date
debug
hash_
math
move_
network
path
platform
query
random
skip
str_
take
url
alias.rs
all.rs
any.rs
append.rs
break_.rs
cal.rs
cd.rs
chunks.rs
compact.rs
complete.rs
config_env_default.rs
config_nu_default.rs
continue_.rs
debug_info.rs
def.rs
default.rs
detect_columns.rs
do_.rs
drop.rs
du.rs
each.rs
echo.rs
empty.rs
error_make.rs
every.rs
exec.rs
export_def.rs
fill.rs
filter.rs
find.rs
first.rs
flatten.rs
for_.rs
format.rs
generate.rs
get.rs
glob.rs
griddle.rs
group_by.rs
headers.rs
help.rs
histogram.rs
ignore.rs
insert.rs
inspect.rs
interleave.rs
into_datetime.rs
into_filesize.rs
into_int.rs
join.rs
last.rs
length.rs
let_.rs
lines.rs
loop_.rs
ls.rs
match_.rs
merge.rs
mktemp.rs
mod.rs
mut_.rs
nu_check.rs
open.rs
par_each.rs
parse.rs
prepend.rs
print.rs
range.rs
redirection.rs
reduce.rs
reject.rs
rename.rs
return_.rs
reverse.rs
rm.rs
roll.rs
rotate.rs
run_external.rs
save.rs
select.rs
semicolon.rs
seq.rs
seq_char.rs
seq_date.rs
sort.rs
sort_by.rs
source_env.rs
split_by.rs
split_column.rs
split_row.rs
table.rs
tee.rs
terminal.rs
to_text.rs
touch.rs
transpose.rs
try_.rs
ucp.rs
ulimit.rs
umkdir.rs
uname.rs
uniq.rs
uniq_by.rs
update.rs
upsert.rs
use_.rs
where_.rs
which.rs
while_.rs
with_env.rs
wrap.rs
zip.rs
format_conversions
main.rs
Cargo.toml
LICENSE
README.md
nu-derive-value
nu-engine
nu-explore
nu-glob
nu-json
nu-lsp
nu-parser
nu-path
nu-plugin
nu-plugin-core
nu-plugin-engine
nu-plugin-protocol
nu-plugin-test-support
nu-pretty-hex
nu-protocol
nu-std
nu-system
nu-table
nu-term-grid
nu-test-support
nu-utils
nu_plugin_custom_values
nu_plugin_example
nu_plugin_formats
nu_plugin_gstat
nu_plugin_inc
nu_plugin_nu_example
nu_plugin_polars
nu_plugin_python
nu_plugin_query
nu_plugin_stress_internals
nuon
README.md
devdocs
docker
scripts
src
tests
wix
.gitattributes
.gitignore
CITATION.cff
CODE_OF_CONDUCT.md
CONTRIBUTING.md
Cargo.lock
Cargo.toml
Cross.toml
LICENSE
README.md
rust-toolchain.toml
toolkit.nu
typos.toml
nushell/crates/nu-command/tests/commands/any.rs
YizhePKU 6c649809d3 Rewrite run_external.rs ()
This PR is a complete rewrite of `run_external.rs`. The main goal of the
rewrite is improving readability, but it also fixes some bugs related to
argument handling and the PATH variable (fixes
https://github.com/nushell/nushell/issues/6011).

I'll discuss some technical details to make reviewing easier.

## Argument handling

Quoting arguments for external commands is hard. Like, *really* hard.
We've had more than a dozen issues and PRs dedicated to quoting
arguments (see Appendix) but the current implementation is still buggy.

Here's a demonstration of the buggy behavior:

```nu
let foo = "'bar'"
^touch $foo            # This creates a file named `bar`, but it should be `'bar'`
^touch ...[ "'bar'" ]  # Same
```

I'll describe how this PR deals with argument handling.

First, we'll introduce the concept of **bare strings**. Bare strings are
**string literals** that are either **unquoted** or **quoted by
backticks** [^1]. Strings within a list literal are NOT considered bare
strings, even if they are unquoted or quoted by backticks.

When a bare string is used as an argument to external process, we need
to perform tilde-expansion, glob-expansion, and inner-quotes-removal, in
that order. "Inner-quotes-removal" means transforming from
`--option="value"` into `--option=value`.

## `.bat` files and CMD built-ins

On Windows, `.bat` files and `.cmd` files are considered executable, but
they need `CMD.exe` as the interpreter. The Rust standard library
supports running `.bat` files directly and will spawn `CMD.exe` under
the hood (see
[documentation](https://doc.rust-lang.org/std/process/index.html#windows-argument-splitting)).
However, other extensions are not supported [^2].

Nushell also supports a selected number of CMD built-ins. The problem
with CMD is that it uses a different set of quoting rules. Correctly
quoting for CMD requires using
[Command::raw_arg()](https://doc.rust-lang.org/std/os/windows/process/trait.CommandExt.html#tymethod.raw_arg)
and manually quoting CMD special characters, on top of quoting from the
Nushell side. ~~I decided that this is too complex and chose to reject
special characters in CMD built-ins instead [^3]. Hopefully this will
not affact real-world use cases.~~ I've implemented escaping that works
reasonably well.

## `which-support` feature

The `which` crate is now a hard dependency of `nu-command`, making the
`which-support` feature essentially useless. The `which` crate is
already a hard dependency of `nu-cli`, and we should consider removing
the `which-support` feature entirely.

## Appendix

Here's a list of quoting-related issues and PRs in rough chronological
order.

* https://github.com/nushell/nushell/issues/4609
* https://github.com/nushell/nushell/issues/4631
* https://github.com/nushell/nushell/issues/4601
  * https://github.com/nushell/nushell/pull/5846
* https://github.com/nushell/nushell/issues/5978
  * https://github.com/nushell/nushell/pull/6014
* https://github.com/nushell/nushell/issues/6154
  * https://github.com/nushell/nushell/pull/6161
* https://github.com/nushell/nushell/issues/6399
  * https://github.com/nushell/nushell/pull/6420
  * https://github.com/nushell/nushell/pull/6426
* https://github.com/nushell/nushell/issues/6465
* https://github.com/nushell/nushell/issues/6559
  * https://github.com/nushell/nushell/pull/6560

[^1]: The idea that backtick-quoted strings act like bare strings was
introduced by Kubouch and briefly mentioned in [the language
reference](https://www.nushell.sh/lang-guide/chapters/strings_and_text.html#backtick-quotes).

[^2]: The documentation also said "running .bat scripts in this way may
be removed in the future and so should not be relied upon", which is
another reason to move away from this. But again, quoting for CMD is
hard.

[^3]: If anyone wants to try, the best resource I found on the topic is
[this](https://daviddeley.com/autohotkey/parameters/parameters.htm).
2024-05-23 02:05:27 +00:00

93 lines
2.3 KiB
Rust

use nu_test_support::{nu, pipeline};
#[test]
fn checks_any_row_is_true() {
let actual = nu!(pipeline(
r#"
echo [ "Ecuador", "USA", "New Zealand" ]
| any {|it| $it == "New Zealand" }
"#
));
assert_eq!(actual.out, "true");
}
#[test]
fn checks_any_column_of_a_table_is_true() {
let actual = nu!(pipeline(
"
echo [
[ first_name, last_name, rusty_at, likes ];
[ Andrés, Robalino, '10/11/2013', 1 ]
[ JT, Turner, '10/12/2013', 1 ]
[ Darren, Schroeder, '10/11/2013', 1 ]
[ Yehuda, Katz, '10/11/2013', 1 ]
]
| any {|x| $x.rusty_at == '10/12/2013' }
"
));
assert_eq!(actual.out, "true");
}
#[test]
fn checks_if_any_returns_error_with_invalid_command() {
// Using `with-env` to remove `st` possibly being an external program
let actual = nu!(pipeline(
r#"
with-env {PATH: ""} {
[red orange yellow green blue purple] | any {|it| ($it | st length) > 4 }
}
"#
));
assert!(
actual.err.contains("Command `st` not found") && actual.err.contains("Did you mean `ast`?")
);
}
#[test]
fn works_with_1_param_blocks() {
let actual = nu!("[1 2 3] | any {|e| print $e | false }");
assert_eq!(actual.out, "123false");
}
#[test]
fn works_with_0_param_blocks() {
let actual = nu!("[1 2 3] | any {|| print $in | false }");
assert_eq!(actual.out, "123false");
}
#[test]
fn early_exits_with_1_param_blocks() {
let actual = nu!("[1 2 3] | any {|e| print $e | true }");
assert_eq!(actual.out, "1true");
}
#[test]
fn early_exits_with_0_param_blocks() {
let actual = nu!("[1 2 3] | any {|| print $in | true }");
assert_eq!(actual.out, "1true");
}
#[test]
fn any_uses_enumerate_index() {
let actual = nu!("[7 8 9] | enumerate | any {|el| print $el.index | false }");
assert_eq!(actual.out, "012false");
}
#[test]
fn unique_env_each_iteration() {
let actual = nu!(
cwd: "tests/fixtures/formats",
"[1 2] | any {|| print ($env.PWD | str ends-with 'formats') | cd '/' | false } | to nuon"
);
assert_eq!(actual.out, "truetruefalse");
}