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

Command does not escape arguments as expected on windows #29494

Open
Diggsey opened this Issue Oct 31, 2015 · 8 comments

Comments

Projects
None yet
9 participants
@Diggsey
Copy link
Contributor

Diggsey commented Oct 31, 2015

Currently the windows implementation of Command will automatically attempt to escape arguments before concatenating them into a single command line string to be passed to CreateProcess. However, the escaping method used doesn't work for many windows command-line tools, including cmd, msiexec and others.

As an example, it is impossible to specify arguments like this:
ARG="some parameter with spaces"
because Command will try to quote the entire thing, whereas msiexec expects only the value to be quoted.

What's required is a way to pass arguments through unchanged: I suggest an arg_raw or arg_unescaped method be added to Command. This function can either be a windows-specific extension, or a no-op on other platforms. The advantage of the latter is that it avoids the need to cfg out code that relies on it at compile time.

@rkruppe

This comment has been minimized.

Copy link
Member

rkruppe commented Oct 31, 2015

Perhaps the implementation can be changed to not be that terrible? I know absolutely nothing about the win32 API, but I do know that Python's subprocess module uses a function called CreateProcess and AFAIK doesn't have this problem.

Edit: Nevermind, a look at the documentation reveals that this function takes a single string as command to be executed.

@Diggsey

This comment has been minimized.

Copy link
Contributor Author

Diggsey commented Oct 31, 2015

At the very least it should strive to perform the exact, minimal inverse of CommandLineToArgvW (a very nontrivial task unfortunately, see: http://blogs.msdn.com/b/twistylittlepassagesallalike/archive/2011/04/23/everyone-quotes-arguments-the-wrong-way.aspx which describes the problems well enough, but the final solution given is actually still wrong!)

This would be enough to fix the problems for me with msiexec and possibly cmd. However, there's no requirement that processes on windows use CommandLineToArgvW at all, and many in fact do not, so regardless a way of passing through arguments unmolested is required.

For reference, the string ARG="some parameter with spaces" does not need escaping: CommandLineToArgvW will return it as a single argument, which at the very least shows that rusts "inverse" function is currently incorrect.

edit: Actually, no it seems it does still need escaping, it may just be that msiexec does not use CommandLineToArgvW.

@brson brson changed the title Command does not handle arguments well on windows. Command does not escape arguments as expected on windows Apr 11, 2017

@retep998

This comment has been minimized.

Copy link
Member

retep998 commented Apr 11, 2017

This could probably be solved by adding a CommandExt method that specifies the entire command line as a single string overriding how arguments would otherwise be specified. Any code that needs behavior other than the default can then use that method to have full control over the command line.

@shepmaster

This comment has been minimized.

Copy link
Member

shepmaster commented Jun 26, 2017

This popped up on Stack Overflow:

let comm = r#""C:\Program Files\Google\Chrome\Application\chrome.exe" https://stackoverflow.com/"#;
let mut cmd = Command::new("cmd");
cmd.arg("/c");
cmd.arg(comm);

As I understand it, it is expected to execute

"cmd" "/c" ""C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" https://stackoverflow.com/"

But instead executes

"cmd" "/c" "\"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\" https://stackoverflow.com/"

Note that the inner " have been escaped.

@mzji

This comment has been minimized.

Copy link

mzji commented Jul 1, 2017

Met this issue too. Do we have any workaround now?

@retep998

This comment has been minimized.

Copy link
Member

retep998 commented Jul 1, 2017

The workaround is calling CreateProcessW yourself. The solution is providing a Windows specific CommandExt that allows specifying a custom command string.

@mzji

This comment has been minimized.

Copy link

mzji commented Jul 2, 2017

@retep998 I changed the argument in the command line to avoid the quotes.

@rivy

This comment has been minimized.

Copy link

rivy commented Nov 18, 2018

I'm now running into this issue while trying to fix env for windows in the coretools repo.

The summary at this point seems to be, at least for Windows, with its some-what crazy command-line quoting, that there are simply some command lines which are valid and used which cannot be generated by the current std::Command (eg, powershell -command "Get-WmiObject -class Win32_Product"; see https://users.rust-lang.org/t/std-process-is-escaping-a-raw-string-literal-when-i-dont-want-it-to/19441 and https://stackoverflow.com/questions/44757893/cmd-c-doesnt-work-in-rust-when-command-includes-spaces).

IMO, there needs to be some way of using std::Command to generate completely arbitrary command lines. Specifically, argument escaping needs to be more controllable by the user. Pushing users to drop back to calling CreateProcessW, although possibly workable, doesn't seem to be the right path to encourage portability in rust.

An addition to the API of std::Command (eg, arg_raw()) such as suggested by @kornelski on the rust-internals discussion board (see <https://internals.rust-lang.org/t/std-process-on-windows-is-escaping-raw-literals-which-causes-problems-with-chaining-commands/8163/7) would likely help solve the issue.

Any thoughts?

And what should be done to push this issue forward?

cc: @rivy

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