41

This is more of an annoyance rather than a problem but I would very much like to understand the semantics here.

All I want to do is to run an arbitrary command on a temporary command-prompt session which itself running under a bash session.

My success rate is 50/50 as some command works as expected whereas others not so much.

I think the problem may lie around arguments not lining up properly (i.e. missing or merged arguments)

I'll try to explain what I mean by weird by a series of commands and responses. (I'm trying to get the word test to be printed on the screen.)

I'm running these under GNU bash, version 3.1.0(1)-release (i686-pc-msys) Bundled with Git-1.8.4:

First attempt:

$ cmd /c echo test
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.
c:\>

Second attempt:

$ cmd '/c echo test'
test"

Third attempt:

$ cmd "/c echo test"
test"

Fourth attempt:

$ cmd /c\ echo\ test
test"

Fifth attempt:

$ cmd "/c echo" test
'echo" test' is not recognized as an internal or external command,
operable program or batch file.

I'd really appreciate any pointers or insights into the behaviors above as this is unintuitional to me and driving me crazy!

Edit: There is another question that appears similar to this, but it really isn't, mainly because it's about running batch files through CMD /C that doesn't require any arguments.

It doesn't really answer my question about how to provide arguments properly to windows command line apps, and even though the examples are about CMD /C, the answer here can be applied to many other Windows command line apps as well.

3
  • Why would you not quote the command you want the command interpreter to execute? Commented Jan 26, 2014 at 0:00
  • 1
    @IgnacioVazquez-Abrams - His first attempt without any quotes fails completely. Most of the remaining attempts "work" except for an unwanted trailing quote in the output.
    – dbenham
    Commented Jan 26, 2014 at 0:07
  • FWIW, the first variant of simply running cmd /c echo works under cygwin. Commented Jan 26, 2014 at 0:12

8 Answers 8

59

This is actually documented in the ReleaseNotes file (in the top level folder of your installed Git for Windows)

Also, extra care has to be paid to pass Windows programs Windows paths, as they have no clue about MSys style POSIX paths -- You can use something like $(cmd //c echo "$POSIXPATH").

If you use cmd //c echo test it works as expected.

$ cmd //c echo test
test

The cause is to do with trying to ensure that posix paths end up being passed to the git utilities properly. For this reason, Git for Windows includes a modified MSYS layer that affects command arguments. You should note that it is not intended that the bash shell and tools provided with Git for Windows be used as general purpose unix tools for Windows. If you want a general purpose unix-style toolset then you should install MSYS or cygwin. The Git Bash shell is setup for working with git and sometimes that shows.

10

After reading this article I've found solution which works for me:

$ cat gvim.sh
cmd << EOD
gvim $@
EOD
$

Windows 8.1, Git (version 1.9.5-preview20141217), GNU bash, version 3.1.20(4)-release (i686-pc-msys).

5
  • 6
    What article? Can you please provide a link. Commented Apr 5, 2015 at 21:23
  • after trying a ridiculous amount of different things (mostly variations on cmd //c "..."), this one was the one that just worked for me. Can't believe it was buried at 0 votes. Commented Dec 17, 2015 at 9:21
  • I'm not a Windows person but I suspect you'll want double quotes around "$@" even on Windows to correctly cope with file names which contain whitespace or literal wildcard characters.
    – tripleee
    Commented Jul 12, 2017 at 19:50
  • @tripleee I tried "$@" and it just made one single argument out of all, which is incorrect. So the proper implementation is with $@ (without quotes). Commented Oct 27, 2020 at 14:17
  • But then it probably breaks if you have nontrivial quoted arguments you want to pass in. The accepted answer looks like a better solution. Or maybe the MSYS_NO_PATHCONV one. Or maybe improve your life and don't use cmd, or Windows.
    – tripleee
    Commented Oct 27, 2020 at 14:40
4

As I explain here, there is an additional alternative when using modern Git for Windows' bash

MSYS_NO_PATHCONV=1 cmd /c echo test

Explanation of each attempt

TL;DR

The unfortunate answer is in Windows, there are many ways arguments can be parsed, and you have to format your output in bash in such a way that is will be reparsed by the windows program it the way it expects

Second, third, and forth attempts are all actually identical

This is the same as (in cmd) > cmd "/c echo test". Windows cmd only uses " quotes, so somewhere between the runtimes, your (in bash) $ cmd '/c echo test' converts all the arguments to "/c echo test" which is happily parses.

Since from a bash point of view, the 2nd/3rd/4th attempt are all the same, they all give the same response. The surprise " is due to how windows parsing only using " and not ', hence it is the same as > cmd "/c echo test"

Fifth attempt

$ cmd "/c echo" test is the same as > cmd /c echo" test. (I'm guessing: The space after the /c is optional, so cmd isn't confused by /c echo being the space being literal due to the first quote.) So it is trying to execute the command echo" test which doesn't exist. The space is interpreted as literal because of the quote. Likewise, if you had done $ cmd "/c echo "test you would get the output "test, because the space is no longer being treats as literal, and no longer part of the command echo

Note: > cmd "/c echo" test and > cmd /c echo" test error the same. My guess here is that cmd parsed everything after the /c on its own, so the initial " has no affect, as parsing starts all over again. Chalk that up to special cmd weirdness.


This can actually be reproduced using python for windows, which has nothing to do with msys/mingw/git/bash/glibc/etc...

python -c "import subprocess; subprocess.Popen(['cmd', '/c echo test'])"
3

I am able to mostly reproduce the problem using gnu bash for Windows.

I can't quite establish a pattern with the first form without any quotes. It seems to work with the Windows ECHO command, but not with other commands like DIR. EDIT - It turns out gnu bash is putting quotes around my command, so echo test becomes "echo" "test". The quotes cause cmd.exe to look for an external command instead of the internal ECHO command. I happen to have "echo.exe", so it appears to run. The odd thing is the quotes around test are not displayed. When I attempt to run the DIR command, it fails entirely because there isn't any DIR.EXE.

The subsequent forms with quotes (except the last one), or escaped spaces, work the same as you are seeing - there is an unwanted trailing quote in the command.

I could not come up with a clean solution. However, I have an ugly hack that should give you the desired result. Simply concatenate a REM command at the end of your command. The REM will comment out the unwanted trailing quote. It is important that there be a space after REM, otherwise REM" will not be recognized as a valid command. Any of the following should work.

$ cmd '/c echo test&rem '
$ cmd "/c echo test&rem "
$ cmd /c\ echo\ test\&rem\ 

Note that the last command has a space after the backslash.

The technique should work for pretty much any command string that you might want to execute via CMD.EXE.

4
  • When you say "first form", are you referring to the one with no quotes? If so how did you manage to run it that way?
    – Arca Artem
    Commented Jan 27, 2014 at 23:53
  • @ArcaArtem - Yes, I mean without quotes. It only works with echo because I have the gnu echo.exe in my PATH, so it is executing that external command. If I try dir then it errors, saying that "dir" (or "dir if arguments supplied) does not exist. If we could stop gnu bash from adding quotes, then it would work fine - but I could not figure out a way.
    – dbenham
    Commented Jan 28, 2014 at 1:51
  • But how does it even try to run those commands at all? All I get is a new command prompt session. Are you using the Bash bundled with Git, or something else?
    – Arca Artem
    Commented Jan 28, 2014 at 9:31
  • Ahh, yes. My version is win-bash_0.8.5(0). I'm not sure where I got it from, other than it did was not bundled with Git. It was a stand-alone download.
    – dbenham
    Commented Jan 28, 2014 at 13:56
2

I noticed git-bash treats the /c argument like a C: drive:

C:\Windows\system32\cmd.exe C:/ echo test

As dbenham found double quotes are added. This echos test" for example:

cmd /c\ echo\ test

I needed the same line (script) to work in git-bash as well as Cygwin Bash. The only ways that work are

cmd /c\ echo\ test\&rem\ 

(note that this line needs to end in a space), and

cmd << EOC
echo test
EOC

So escape every space after the /c and add \&rem\  at the end of the line (including the trailing space), or just wrap the command in a here document.

All this probably depends on the version of git-bash and the specific commands. :-(

1

Because you mention that you're using the Git for Windows bundle, I figured I'd point out that it includes winpty, which seems to be quite readable.

$ winpty echo test
test

$ site="Default Web Site"
$ winpty 'C:\Windows\System32\inetsrv\appcmd' list site "${site}" /text:ID
1
0

This seems to work under 1.9.5.msysgit.1

!foo=`bar`
cmd //c \\\\unc-path\\with\\slashes -args \"Quoted Arguments $foo\"
0

i had success this way if it helps : You echo win paths with spaces in dirnames by escaping the ", then win parameters

$ echo "\"c:\Program Files (x86)\SysinternalsSuite\streams.exe\" -nobanner " | cmd

Microsoft Windows [version 10.0.19044.4412]
(c) Microsoft Corporation. Tous droits réservés.

usage: c:\Program Files (x86)\SysinternalsSuite\streams.exe [-s] [-d] <file or directory>
-s     Recurse subdirectories
-d     Delete streams
-nobanner
       Do not display the startup banner and copyright message.
$ <-- and get back to bash

Not the answer you're looking for? Browse other questions tagged or ask your own question.