Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

when execute external command, escape args if possible #6560

Merged
merged 1 commit into from
Sep 17, 2022

Conversation

WindSoilder
Copy link
Collaborator

@WindSoilder WindSoilder commented Sep 15, 2022

Description

Fixes: #6559

When passing argument to external command, try escape it.

Relative history:
#6014, in this change, we move trim quote logic into run_external eval stage, but escape logic is also removed.

This pr is going to keep escape logic when parsing arguments.

Tests

Make sure you've done the following:

  • Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
  • Try to think about corner cases and various ways how your changes could break. Cover them with tests.
  • If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.

Make sure you've run and fixed any issues with these commands:

  • cargo fmt --all -- --check to check standard code formatting (cargo fmt --all applies these changes)
  • cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect to check that you're using the standard code style
  • cargo test --workspace --features=extra to check that all the tests pass

@WindSoilder WindSoilder marked this pull request as ready for review September 15, 2022 03:12
@fdncred
Copy link
Collaborator

fdncred commented Sep 15, 2022

Does this PR change any of these tests? https://hackmd.io/9PZKGOs0Rc-5kf8GuVY8rw?view

@WindSoilder
Copy link
Collaborator Author

WindSoilder commented Sep 15, 2022

Nope, it only handle for double quoted string with escape character like: ^echo "\"aa"

@fdncred
Copy link
Collaborator

fdncred commented Sep 15, 2022

I get this on Windows, which I'm guessing is wrong.
image

And using Window's version of echo
image

I wonder how we make this work for all echo versions

@WindSoilder
Copy link
Collaborator Author

WindSoilder commented Sep 15, 2022

I don't have a windows pc(again), I'll try to have a look on weekend

@WindSoilder
Copy link
Collaborator Author

WindSoilder commented Sep 17, 2022

@fdncred Sorry, currently I have to give up for make it compatible with all echo version.

I'd like to share what I get:

  1. For git bash, cmd, I believe there are two styles of argument passing mechanism, powershell is compatible with cmd style argument passing, I believe relative code lays here: https://github.com/PowerShell/PowerShell/blob/9213b7765927299311579b4d3416677186c836e5/src/System.Management.Automation/engine/NativeCommandProcessor.cs#L605-L613
  2. But powershell doesn't compatible with git bash argument passing mechanism, so it doesn't work well with git-bash's echo.

Windows echo and git-bash's echo are two built-in commands for each shell, the idle solution would make nu compatible with different shell's argument passing mechanism for their built-in command.

To handle this, we need to know if the given command is a cmd built-in command, or git-bash built-in command. But I don't think we can handle both, on windows, if we spawn external command failed, currently we think this maybe a cmd built-in command, and try to spawn it with cmd.exe /c xxx again.

Though, I'd propose to make this pr ready to review again, at least it solves external command (not includes other shell's built-in command) with escaping like this example: #6420 (comment)

For other complicate part, it's tracking by the following issue:
#6465

@WindSoilder WindSoilder marked this pull request as ready for review September 17, 2022 07:16
@fdncred
Copy link
Collaborator

fdncred commented Sep 17, 2022

@WindSoilder ok. We could also check which echo it is by invoking which ^echo and looking at the path. That's kind of a hack but it may be helpful.

@fdncred fdncred merged commit 5491634 into nushell:main Sep 17, 2022
@WindSoilder WindSoilder deleted the escape_arg branch September 18, 2022 11:27
IanManske pushed a commit that referenced this pull request May 23, 2024
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
#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.

* #4609
* #4631
* #4601
  * #5846
* #5978
  * #6014
* #6154
  * #6161
* #6399
  * #6420
  * #6426
* #6465
* #6559
  * #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).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

string with escape doesn't work for external command
2 participants