在Git for Windows中使用Bash:在使用带参数的CMD.exe /C命令时出现奇怪的情况。

41

这更像是一种烦恼而不是问题,但我非常想了解这里的语义。

我想做的就是在一个运行在bash会话下的临时命令提示符会话中运行任意命令。

我的成功率是50/50,因为有些命令按预期工作,而其他命令则不尽如人意。

我认为问题可能在于参数没有正确对齐(即缺失或合并的参数)。

我将尝试通过一系列命令和响应来解释我所谓的奇怪。(我正在尝试让单词test在屏幕上打印出来。)

我在GNU bash, version 3.1.0(1)-release (i686-pc-msys) Bundled with Git-1.8.4下运行这些命令:

第一次尝试:

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

第二次尝试:

$ cmd '/c echo test'
test"

第三次尝试:

$ cmd "/c echo test"
test"

第四次尝试:

$ cmd /c\ echo\ test
test"
第五次尝试:
$ cmd "/c echo" test
'echo" test' is not recognized as an internal or external command,
operable program or batch file.

由于这些行为对我来说非常不直观,让我感到很烦恼!我真的很感激任何指导或对这些行为的深入理解。

编辑: 有一个类似的问题,但实际上并不相同,主要是因为它是关于通过CMD /C运行不需要任何参数的批处理文件。它并没有真正回答我的问题,即如何正确地向Windows命令行应用程序提供参数,即使例子是关于CMD /C的,这里的答案也可以适用于许多其他Windows命令行应用程序。


为什么你不给想要命令解释器执行的命令加引号? - Ignacio Vazquez-Abrams
@IgnacioVazquez-Abrams - 他第一次尝试没有任何引号完全失败了。其余的大部分尝试“工作”,除了输出中不需要的尾随引号。 - dbenham
就此而言,最简单的运行 cmd /c echo 变体在 cygwin 下可以工作。 - mockinterface
7个回答

56

实际上,在您安装的Git for Windows的顶层文件夹中的ReleaseNotes文件中已经记录了这一点。

此外,必须特别注意传递Windows程序的Windows路径,因为它们对MSys风格的POSIX路径一无所知——您可以使用类似于 $(cmd //c echo "$POSIXPATH") 的内容。

如果使用 cmd //c echo test,则会按预期工作。

$ cmd //c echo test
test

原因是为了确保 posix 路径被正确地传递给 git 实用程序。出于这个原因,Git for Windows 包括一个修改过的 MSYS 层,影响命令参数。请注意,不打算将 Git for Windows 提供的 bash shell 和工具用作 Windows 上的通用 unix 工具。如果您想要一个通用的 unix 风格的工具集,那么您应该安装 MSYS 或 Cygwin。Git Bash shell 被设置为与 git 一起使用,有时会显示出来。


10

阅读本文后,我找到了适合我的解决方案:

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

Windows 8.1、Git(版本1.9.5-preview20141217)、GNU Bash(版本3.1.20(4)-release(i686-pc-msys))。


6
什么文章?可以请您提供链接。 - PiotrWolkowski
在尝试了大量不同的方法之后(主要是 cmd //c "..." 的变体),这个方法是唯一对我有效的。真不敢相信它只有0票。 - David Goldstein
我不是一个Windows专家,但我猜你可能需要在"$@"周围加上双引号,即使在Windows上,以正确处理包含空格或通配符字符的文件名。 - tripleee
@tripleee 我尝试使用 "$@",但它将所有参数合并成了一个单一的参数,这是错误的。因此,正确的实现方式是使用 $@(不用引号)。 - Dmitry Kashtanov
但是,如果您要传递非平凡的带引号参数,则可能会出现问题。 接受的答案看起来更像是更好的解决方案。 或者也可以使用 MSYS_NO_PATHCONV。 或者改善您的生活,不要使用 cmd 或 Windows。 - tripleee

4

正如我这里所解释的那样,在使用现代的Git for Windows的bash时,还有一个额外的选择。

MSYS_NO_PATHCONV=1 cmd /c echo test

每次尝试的解释

简短版

不幸的是,在Windows中,有许多方法可以解析参数,您必须以一种bash能够重新解析的方式格式化输出,以便Windows程序可以按照其期望的方式重解析它。

第二、第三、第四次尝试实际上完全相同

这与(在cmd中)> cmd "/c echo test"相同。Windows cmd只使用"引号,因此在运行时,您的(在bash中)$ cmd '/c echo test'将所有参数转换为"/c echo test",它会被愉快地parses

从bash的角度来看,第二/三/四次尝试都是相同的,它们都会给出相同的响应。惊喜"是由于Windows解析只使用"而不是',因此它与> cmd "/c echo test"相同。

第五次尝试

$ cmd "/c echo" test> cmd /c echo" test相同。(我猜测:在/c后面的空格是可选的,因此cmd不会将/c echo解释为字面上的空格,因为第一个引号。) 因此,它正在尝试执行不存在的命令echo" test。由于引号的存在,空格被解释为字面上的空格。同样,如果您执行了$ cmd "/c echo "test,您将获得输出"test,因为空格不再被视为字面上的空格,也不再是echo命令的一部分。

注意:> cmd "/c echo" test> cmd /c echo" test 错误相同。我猜测这里是 cmd 自己解析了 /c 后面的所有内容,所以最初的 " 没有影响,因为解析重新开始。把这归结为特殊的 cmd 奇怪行为。
这实际上可以使用适用于Windows的Python进行复制,与msys/mingw/git/bash/glibc等无关...
python -c "import subprocess; subprocess.Popen(['cmd', '/c echo test'])"

3

我能够在Windows上使用gnu bash大部分地复现该问题。

对于没有引号的第一种形式,我无法确立一个模式。它似乎可以与Windows ECHO命令一起工作,但不能与其他命令如DIR一起工作。 编辑 - 原来gnu bash在我的命令周围加上了引号,所以echo test变成了"echo" "test"。引号导致cmd.exe寻找外部命令而不是内部ECHO命令。我恰好有"echo.exe",所以它似乎能运行。奇怪的是,test周围的引号没有显示出来。当我尝试运行DIR命令时,它完全失败,因为没有DIR.EXE。

带引号(除了最后一个)或转义空格的后续形式与您看到的相同-命令中有一个不需要的尾随引号。

我找不到一个干净的解决方案。但是,我有一个丑陋的hack,应该能给你想要的结果。只需在命令末尾连接REM命令即可。 REM将注释掉不需要的尾随引号。重要的是,在REM之后有一个空格,否则REM"将不被识别为有效命令。以下任何一种都应该可以。

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

请注意,最后一个命令在反斜杠后面有一个空格。
这种技术应该适用于几乎任何您想通过CMD.EXE执行的命令字符串。

当你说“第一个表单”时,你是指没有引号的那个吗?如果是这样,你是如何以那种方式运行它的? - Arca Artem
@ArcaArtem - 是的,我的意思是不带引号。它只能与echo一起使用,因为我在我的PATH中有gnu echo.exe,所以它正在执行该外部命令。如果我尝试使用dir,则会出错,说“dir”(或如果提供了参数,则为“dir”)不存在。如果我们可以阻止gnu bash添加引号,那么它将正常工作-但我无法找到方法。 - dbenham
但它是如何尝试运行这些命令的呢?我只得到了一个新的命令提示符会话。你是在使用 Git 捆绑的 Bash,还是其他什么东西? - Arca Artem
啊,是的。我的版本是win-bash_0.8.5(0)。我不确定它是从哪里得到的,除了它没有与Git捆绑在一起。它是一个独立的下载。 - dbenham

2
我注意到git-bash将/c参数视为C驱动器:
C:\Windows\system32\cmd.exe C:/ echo test

正如dbenham发现的那样,双引号被添加了。例如,这个命令会输出test":

cmd /c\ echo\ test

我希望您能在git-bash和Cygwin Bash中运行相同的脚本。唯一可行的方法是:
cmd /c\ echo\ test\&rem\ 

请注意,这一行需要以空格结尾,并且

cmd << EOC
echo test
EOC

因此,在/c之后逃脱每个空格并在行末(包括尾随空格)添加\&rem\ 或者将命令包装在here document中。所有这些可能都取决于git-bash的版本和特定的命令。 :-(


1
因为你提到正在使用 Git for Windows 包,所以我想指出它包含 winpty,看起来非常易读。
$ winpty echo test
test

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

0

这似乎在1.9.5.msysgit.1下工作。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接