m19c / gulp-run

Pipe to shell commands in gulp
ISC License
151 stars 25 forks source link

Cannot call program with spaces in path on Windows #23

Open borekb opened 9 years ago

borekb commented 9 years ago

I have a code like this:

$command = '"C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\IDE\\devenv.exe" "..\\MySolution.sln" /run';
run($command).exec();

However, it fails with

'\"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.exe\"' is not recognized as an internal or external command, operable program or batch file.

I think the string is quoted correctly in my code, isn't it?

cbarrick commented 9 years ago

Unfortunately, I don't have a Windows machine to work on this. Does this happen with the usePowerShell option?

borekb commented 9 years ago

The escaping is wrong in both cases. The problem is on this line: https://github.com/cbarrick/gulp-run/blob/master/lib/command.js#L79 where the command is properly quoted but but childProcess.spawn() maybe transforms it somehow, I don't know, but when I take the output of this line: https://github.com/cbarrick/gulp-run/blob/master/lib/command.js#L99 and just copy it to cmd.exe like this:

cmd /c <copy/pasted command here>

it works. So it must be something inside the childProcess.spawn() I think.

BTW Windows 10 is available for free so if you need a testing VM it should be relatively simple to create one, compared to the past.

cbarrick commented 9 years ago

On Unix, sh -c COMMAND expects all of the command to be a single argument to sh (i.e. enclosed in quotes when typing into a terminal), but on Windows it seems that cmd /c COMMAND expects each argument of the command to be a separate argument to cmd.exe. So the command string needs to be split into discrete arguments on Windows before handing it off to childProcess.spawn().

That's my hunch anyway. I'll look into setting up a Windows VM to test it out.

Source: http://ss64.com/nt/cmd.html

cbarrick commented 9 years ago

According to that same site,powershell -Command COMMAND expects the command as a single argument, so you may be able to setup powershell as a workaround until this gets fixed.

cbarrick commented 9 years ago

I pushed an untested fix as c5ad8a0. When I test it, and if it works, I'll push it to npm.

borekb commented 9 years ago

Another interesting test case that just came to mind - the command can be something like:

cmd1.exe && cmd2.exe

This executes fine on cmd.exe, like:

cmd /c cmd1.exe && cmd2.exe

and so it should on gulp-run as well. Thanks for your effort!

cbarrick commented 9 years ago

That use case is definitely important to this project. Actually, that's why I chose to run commands through a subshell rather than executing them directly. All higher-level shell features are supposed to work, so if they don't, it's a bug.

That being said, I'm a total Windows noob and have no idea how higher-level shell features are handled in cmd.exe. Upon further reading, I may have been wrong about my previous conclusions.

If /C or /K is specified, then the remainder of the command line is processed as an immediate command in the new shell. Multiple commands separated by the command separator '&' or '&&' are accepted if surrounded by quotes.

So it seems that commands SHOULD work as both a single argument and multiple arguments, but they MUST be single arguments to support higher-level shell features like &&. However, I don't know how cmd.exe passes arguments, so I don't know if the quotes get passed along. I'll have to do some more testing to see how the quotes should be handled.