Make command line escaping on Windows not add superfluous double quotes #347
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR is an attempt to fix #51.
As explained there,
System.Process
currently adds superfluous double quotes when assembling command lines on Windows. For example, if I runreadCreateProcess (proc "notepad.exe" ["foo.txt"]) ""
, then Windows will see a command line like"notepad.exe" "foo.txt"
.This quoting is extra-conservative, but it also causes a number of problems which people have reported in #51. In my case, it makes it impossible to run something like
readCreateProcess (proc "wsl.exe" ["--help"]) "
, because the quotes confusewsl.exe
and it tries to go into shell mode and execute commands on a VM. The quoting is also contrary to what other common process libraries do, like Python'ssubprocess.run
.Correctness
To show that this alternative implementation is correct, I wrote some tests in this repo: https://github.com/thomasjm/process-escape-windows. I test in the following ways:
CommandLineToArgvW
FFI. Using FFI, I assemble random executable names and argument strings and test that the WindowsCommandLineToArgvW
function parses them correctly (with QuickCheck).cmd.exe /c child.exe <args>
, wherechild.exe
is a specially crafted C program that echoes back its arguments. We have to apply an extra level of escaping forcmd.exe
for this to work. This is also tested using QuickCheck.translateInternal
from this repo using FFI.Conclusions:
.bat
files; see below)About
.bat
and.cmd
filesThis package also has a function called
translateCmdExeArg
, which is applied when you useRawCommand
and an executable name that ends with.bat
or.cmd
.I tried for a while to make a version of
translateCmdExeArg
that will work for arbitrary arguments, tested with QuickCheck--but ultimately succumbed to despair. Look at how long and crazy a typical StackOverflow answer is on this subject: https://stackoverflow.com/a/31413730.For this reason, I haven't touched
translateCmdExeArg
in this PR. I immediately guessed that it would fail for arguments that contain double quotes. For example, I tested runningreadCreateProcess (proc childBat ["a\"b"]) ""
using a recent Stackage resolver, usingchild.bat
in my test repo, which echoes back the commands it receives. It left a random caret in the result:a"b^
.It's good that
translateCmdExeArg
has a BatBadBut remediation, but I would say users shouldn't expect it to pass their arguments faithfully through to a batch script if the arguments have unusual characters etc.Conclusion
Hopefully this is enough validation to support merging this PR! We could also potentially bring my tests over to this package, but that would involve adding a
QuickCheck
dependency.