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

On Windows, find executables the same way the shell normally does. #1564

Open
trytriangles opened this issue May 22, 2024 · 1 comment
Open

Comments

@trytriangles
Copy link

On Windows, if you have hello.py on your PATH and Python files designated as executable, you can it from the shell directly with and without its file extension:

$ hello world
Hello, world!

$ hello.py Rusty
Hello, Rusty!

It doesn't work in fd, however:

$ fd --exec hello
[fd error]: Command not found: hello

$ fd --exec hello.py
[fd error]: Problem while executing command: %1 is not a valid Win32 application.

I propose that on Windows fd's --exec be modified to find and execute programs the same way the regular shell does. This seems like the more expected and desirable behavior.

It can also avoid some potentially destructive situations when two programs of identical name exist, which can be common when things like Busybox are installed. Many shims are implemented as .ps1, .cmd, .bat or .ps1 files and users may find themselves executing unexpected things (e.g. I found that fd --exec ln gets me a shim to mklink, which takes (link_name, target), when ln on its own gets me coreutils ln, which takes (target, link_name).

I half-implemented this before reading the contributing guidelines and seeing the suggestion to open an issue before making a pull request, so here I am. There are two questions and concerns I'd have:

  • Doing this involves reading one to two values from the registry. If implemented, would it be preferable to do this using one of the pre-existing crates for registry interaction (adding an OS-specific dependency) or with a foreign-function call to the Windows API (adding an unsafe block)?
  • This would be an OS-specific behavior slightly complicating automated testing.

The registry keys associated with it have been there unchanged for at least 27 years so OS versions shouldn't be an issue.

@tavianator
Copy link
Collaborator

  • Doing this involves reading one to two values from the registry. If implemented, would it be preferable to do this using one of the pre-existing crates for registry interaction (adding an OS-specific dependency) or with a foreign-function call to the Windows API (adding an unsafe block)?

Ideally we would use a crate that does this for us, since the maintainers of that crate probably have more Windows expertise than we do.

But also, which registry keys are you talking about? I thought that the implicit executable extensions came from the PATHEXT environment variable.

Take this with a grain of salt because I haven't used Windows in many years, but I expect the ideal algorithm is something like this:

  • Resolve the executable against the PATH taking PATHEXT into account (with e.g. https://crates.io/crates/which). So hello will become path\to\hello.py, explorer becomes path\to\explorer.exe, etc.
    • If we end up with a path whose extension is not in PATHEXT, bail out (which may do this already for us, not sure). The goal is to support --exec hello.py but not --exec hello.txt.
  • Run the executable with ShellExecute(), not CreateProcess(). That will pick up the right file associations for script interpreters.

I can think of two complications:

  • We use https://github.com/sharkdp/argmax for --exec-batch, but it only knows about CreateProcess(), not ShellExecute().
  • There are security issues with command execution that Rust is handling for us right now. Presumably ShellExecute() has more potential issues and we'd have to be more careful ourselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants