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

Restore tilde expansion on external command names #13001

Merged
merged 1 commit into from
May 30, 2024

Conversation

devyn
Copy link
Contributor

@devyn devyn commented May 30, 2024

Description

Fix a regression introduced by #12921, where tilde expansion was no longer done on the external command name, breaking things like

> ~/.cargo/bin/exa

This properly handles quoted strings, so they don't expand:

> ^"~/.cargo/bin/exa"
Error: nu::shell::external_command

  × External command failed
   ╭─[entry #1:1:2]
 1  ^"~/.cargo/bin/exa"
   ·  ─────────┬────────
   ·           ╰── Command `~/.cargo/bin/exa` not found
   ╰────
  help: `~/.cargo/bin/exa` is neither a Nushell built-in or a known external command

This required a change to the parser, so the command name is also parsed in the same way the arguments are - i.e. the quotes on the outside remain in the expression. Hopefully that doesn't break anything else. 🤞

Fixes #13000. Should include in patch release 0.94.1

cc @YizhePKU

User-Facing Changes

  • Tilde expansion now works again for external commands
  • The command of run-external will now have its quotes removed like the other arguments if it is a literal string
  • The parser is changed to include quotes in the command expression of ExternalCall if they were present

Tests + Formatting

I would like to add a regression test for this, but it's complicated because we need a well-known binary within the home directory, which just isn't a thing. We could drop one there, but that's kind of a bad behavior for a test to do. I also considered changing the home directory for the test, but that's so platform-specific - potentially could get it working on specific platforms though. Changing HOME env on Linux definitely works as far as tilde expansion works.

  • 🟢 toolkit fmt
  • 🟢 toolkit clippy
  • 🟢 toolkit test
  • 🟢 toolkit test stdlib

@fdncred
Copy link
Collaborator

fdncred commented May 30, 2024

Running through some quick scenarios and stumbled upon this one that I think should work but doesn't.

We should be able to construct a path to an external and execute it, I believe.

let bin = "pastel"  #<- fill this in with whatever bin you have in your .cargo/bin
 ^$"~/.cargo/bin/($bin)"
Error: nu::shell::external_command

  × External command failed
   ╭─[entry #7:1:2]
 1  ^$"~/.cargo/bin/($bin)"
   ·  ───────────┬──────────
   ·             ╰── Command `~/.cargo/bin/pastel` not found
   ╰────
  help: `~/.cargo/bin/pastel` is neither a Nushell built-in or a known external command

but this works fine

 ~/.cargo/bin/pastel
pastel 0.9.0
A command-line tool to generate, analyze, convert and manipulate colors

@fdncred
Copy link
Collaborator

fdncred commented May 30, 2024

Related to the PR but not my last comment necessarily, changing quoting rules kind of worries me. As I recall, windows/cmd has some very strange quoting scenarios. Not sure about other operating systems. What I've learned about quoting is it's less about doing what's "right" and more about doing what other commands expect like cmd.exe, bash, etc.

@devyn
Copy link
Contributor Author

devyn commented May 30, 2024

We don't usually expand tilde and glob on quoted strings, and interpolated strings count. So I think it's most consistent this way. For example:

> ^echo ~/.cargo/bin/exa
/home/devyn/.cargo/bin/exa
> ^echo "~/.cargo/bin/exa"
~/.cargo/bin/exa
> ^echo $"~/.cargo/bin/exa"
~/.cargo/bin/exa

I just changed it to work the same way the other arguments do (using the same code), so if there's a bug there (e.g. with external escaping) it'll also be a bug here, but we also only have one place to fix it.

@IanManske
Copy link
Member

Your ^$"~/.cargo/bin/($bin)" example @fdncred does indeed work in previous versions, but this doesn't match our current expansion rules. E.g., ls "~/" doesn't expand the tilde, and it probably shouldn't be expanded for the external command either.

(@devyn beat me to it lol)

@fdncred
Copy link
Collaborator

fdncred commented May 30, 2024

If it's an interpolated string (I think) we need to expand it. This is a very common thing in nushell. Constructing a path to an executable so you can run it.

@devyn
Copy link
Contributor Author

devyn commented May 30, 2024

Hmm, but it isn't the same behaviour we use for arguments. In that case, should I just change it so that it doesn't attempt to detect bare strings at all, and always expands?

@fdncred
Copy link
Collaborator

fdncred commented May 30, 2024

With regards to my previous comment about string interpolation and expansion, I'm fine with going along with those changes. And since devyn assures me that these quoting rules don't touch things like cmd.exe's builtin mklink, I'm good to go here.

@devyn devyn merged commit 0e15530 into nushell:main May 30, 2024
13 checks passed
@devyn devyn deleted the expand-tilde-in-run-external branch May 30, 2024 01:48
@hustcer hustcer added this to the v0.95.0 milestone May 30, 2024
@YizhePKU
Copy link
Contributor

YizhePKU commented May 30, 2024

This required a change to the parser, so the command name is also parsed in the same way the arguments are - i.e. the quotes on the outside remain in the expression.

Do we really need to do that? Ideally, we want to be removing hacks like this, not adding more of it.

Expanding the command name is just different from expanding arguments, and we don't have to force them to be the same. For example, we perform glob expansion on arguments, but it doesn't make sense to perform glob expansion on the command name.

If it's an interpolated string (I think) we need to expand it. This is a very common thing in nushell. Constructing a path to an executable so you can run it.

I agree.

YizhePKU added a commit to YizhePKU/nushell that referenced this pull request May 30, 2024
@devyn
Copy link
Contributor Author

devyn commented May 30, 2024

Do we really need to do that? Ideally, we want to be removing hacks like this, not adding more of it.

Right, I completely agree. I just want something that has the behavior we want for a patch release, and then we can try to make it less hacky after. This seemed like the most consistent option (for now) even though it's being more consistently janky.

devyn added a commit that referenced this pull request Jun 20, 2024
…he parser (#13089)

# Description

We've had a lot of different issues and PRs related to arg handling with
externals since the rewrite of `run-external` in #12921:

- #12950
- #12955
- #13000
- #13001
- #13021
- #13027
- #13028
- #13073

Many of these are caused by the argument handling of external calls and
`run-external` being very special and involving the parser handing
quoted strings over to `run-external` so that it knows whether to expand
tildes and globs and so on. This is really unusual and also makes it
harder to use `run-external`, and also harder to understand it (and
probably is part of the reason why it was rewritten in the first place).

This PR moves a lot more of that work over to the parser, so that by the
time `run-external` gets it, it's dealing with much more normal Nushell
values. In particular:

- Unquoted strings are handled as globs with no expand
- The unescaped-but-quoted handling of strings was removed, and the
parser constructs normal looking strings instead, removing internal
quotes so that `run-external` doesn't have to do it
- Bare word interpolation is now supported and expansion is done in this
case
- Expressions typed as `Glob` containing `Expr::StringInterpolation` now
produce `Value::Glob` instead, with the quoted status from the expr
passed through so we know if it was a bare word
- Bare word interpolation for values typed as `glob` now possible, but
not implemented
- Because expansion is now triggered by `Value::Glob(_, false)` instead
of looking at the expr, externals now support glob types

# User-Facing Changes

- Bare word interpolation works for external command options, and
otherwise embedded in other strings:
  ```nushell
  ^echo --foo=(2 + 2) # prints --foo=4
  ^echo -foo=$"(2 + 2)" # prints -foo=4
  ^echo foo="(2 + 2)" # prints (no interpolation!) foo=(2 + 2)
  ^echo foo,(2 + 2),bar # prints foo,4,bar
  ```

- Bare word interpolation expands for external command head/args:
  ```nushell
  let name = "exa"
  ~/.cargo/bin/($name) # this works, and expands the tilde
  ^$"~/.cargo/bin/($name)" # this doesn't expand the tilde
  ^echo ~/($name)/* # this glob is expanded
  ^echo $"~/($name)/*" # this isn't expanded
  ```

- Ndots are now supported for the head of an external command
(`^.../foo` works)

- Glob values are now supported for head/args of an external command,
and expanded appropriately:
  ```nushell
  ^("~/.cargo/bin/exa" | into glob) # the tilde is expanded
  ^echo ("*.txt" | into glob) # this glob is expanded
  ```

- `run-external` now works more like any other command, without
expecting a special call convention
  for its args:
  ```nushell
  run-external echo "'foo'"
  # before PR: 'foo'
  # after PR:  foo
  run-external echo "*.txt"
  # before PR: (glob is expanded)
  # after PR:  *.txt
  ```

# Tests + Formatting
Lots of tests added and cleaned up. Some tests that weren't active on
Windows changed to use `nu --testbin cococo` so that they can work.
Added a test for Linux only to make sure tilde expansion of commands
works, because changing `HOME` there causes `~` to reliably change.

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`

# After Submitting
- [ ] release notes: make sure to mention the new syntaxes that are
supported
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.

0.94 regression: Cannot invoke a binary with a path that starts with ~
5 participants