用于 Windows 的 Git 中的 Bash:使用 CMD.exe /C 和 args 运行命令时的怪异

Posted

技术标签:

【中文标题】用于 Windows 的 Git 中的 Bash:使用 CMD.exe /C 和 args 运行命令时的怪异【英文标题】:Bash in Git for Windows: Weirdness when running a command with CMD.exe /C with args 【发布时间】:2014-02-16 22:20:54 【问题描述】:

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

我想要做的就是在临时命令提示会话上运行任意命令,该会话本身在 bash 会话下运行。

我的成功率为 50/50,因为某些命令按预期工作,而其他命令则不然。

我认为问题可能在于没有正确排列的参数(即缺少或合并的参数)

我将尝试通过一系列命令和响应来解释我所说的奇怪的意思。 (我试图让 test 这个词打印在屏幕上。)

我在 GNU bash,版本 3.1.0(1)-release (i686-pc-msys) 与 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.

我非常感谢对上述行为的任何指示或见解,因为这对我来说是非直觉的,让我发疯!

编辑: another question 看起来与此类似,但实际上并非如此,主要是因为它是关于通过不需要任何参数的 CMD /C 运行批处理文件。

它并没有真正回答我关于如何正确地向 Windows 命令行应用程序提供参数的问题,即使这些示例是关于 CMD /C 的,这里的答案也可以应用于许多其他 Windows 命令行应用程序。

【问题讨论】:

为什么不引用您希望命令解释器执行的命令? @IgnacioVazquez-Abrams - 他的第一次没有任何引号的尝试完全失败了。除了输出中不需要的尾随引号之外,其余的大多数尝试都“有效”。 FWIW,第一个简单运行 cmd /c echo 的变体在 cygwin 下工作。 【参考方案1】:

我能够使用 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 执行的任何命令字符串。

【讨论】:

当您说“第一种形式”时,您指的是没有引号的形式吗?如果是这样,您是如何设法以这种方式运行它的? @ArcaArtem - 是的,我的意思是没有引号。它仅适用于 echo,因为我的 PATH 中有 gnu echo.exe,因此它正在执行该外部命令。如果我尝试dir,那么它会出错,说"dir"(或"dir,如果提供了参数)不存在。如果我们可以阻止 gnu bash 添加引号,那么它会正常工作 - 但我想不出办法。 但是它是如何尝试运行这些命令的呢?我得到的只是一个新的命令提示符会话。您使用的是与 Git 捆绑的 Bash,还是其他? 啊,是的。我的版本是 win-bash_0.8.5(0)。我不确定我从哪里得到它,除了它没有与 Git 捆绑在一起。这是一个独立的下载。【参考方案2】:

这实际上记录在 ReleaseNotes 文件中(在您安装的 Windows 版 Git 的***文件夹中)

此外,传递 Windows 程序的 Windows 路径必须格外小心,因为它们不知道 MSys 风格的 POSIX 路径——你可以使用类似 $(cmd //c echo "$POSIXPATH") 的东西。

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

$ cmd //c echo test
test

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

【讨论】:

【参考方案3】:

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

$ 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)。

【讨论】:

什么文章?能否给个链接。 在尝试了很多不同的东西(主要是cmd //c "..." 的变体)之后,这个对我有用。不敢相信它以 0 票被埋葬。 我不是 Windows 用户,但我怀疑即使在 Windows 上您也需要在 "$@" 周围加上双引号,以正确处理包含空格或文字通配符的文件名。 @tripleee 我试过"$@",但它只提出了一个论点,这是不正确的。所以正确的实现是使用$@(不带引号)。 但是,如果您想要传递非平凡的引用参数,它可能会中断。接受的答案看起来是一个更好的解决方案。或者可能是MSYS_NO_PATHCONV 之一。或者也许改善你的生活,不要使用cmd 或 Windows。【参考方案4】:

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

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

【讨论】:

【参考方案5】:

我注意到 git-bash 将 /c 参数视为 C: 驱动器:

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

添加了dbenham found 双引号。这与 test" 相呼应,例如:

cmd /c\ echo\ test

我需要同一行(脚本)在 git-bash 和 Cygwin Bash 中工作。唯一可行的方法是

cmd /c\ echo\ test\&rem\ 

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

cmd << EOC
echo test
EOC

所以在/c添加 \&amp;rem\  在行尾 之后转义每个空格(包括尾随空格) ,或者只是将命令包装在 here document 中。

这一切可能取决于 git-bash 的版本和具体的命令。 :-(

【讨论】:

【参考方案6】:

因为您提到您正在使用 Git for Windows 捆绑包,所以我想我会指出 it includes winpty,这似乎很容易阅读。

$ winpty echo test
test

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

【讨论】:

【参考方案7】:

正如我所解释的 here,在使用现代 Git for Windows 的 bash 时还有另外一种选择

MSYS_NO_PATHCONV=1 cmd /c echo test

每次尝试的解释

TL;DR

不幸的答案是在 Windows 中,有很多方法可以解析参数,你必须在 bash 中格式化你的输出,这样 Windows 程序就会以 it 的方式重新解析它。 em> 期望

第二次、第三次和第四次尝试实际上都是相同的

这与(在 cmd 中)&gt;cmd "/c echo test" 相同。 Windows cmd 仅使用 " 引号,因此在运行时之间的某个位置,您的(在 bash 中)$ cmd '/c echo test' 将所有参数转换为 "/c echo test",这很高兴是 parses。

因为从 bash 的角度来看,第 2 次/第 3 次/第 4 次尝试都是相同的,它们都给出相同的响应。惊喜" 是由于windows 解析只使用" 而不是',因此它与&gt; cmd "/c echo test" 相同

第五次尝试

$ cmd "/c echo" test&gt; cmd /c echo" test 相同。 (我猜:/c 之后的空格是可选的,所以 cmd 不会被 /c echo 混淆,因为第一个引号是文字空间。)所以它正在尝试执行命令 echo" test不存在。由于引用,空格被解释为文字。同样,如果你已经完成了$cmd "/c echo "test,你会得到输出"test,因为空格不再被视为文字,并且不再是命令echo的一部分

注意:&gt; cmd "/c echo" test&gt; cmd /c echo" test 错误相同。我的猜测是 cmd 会自行解析 /c 之后的所有内容,因此初始的 " 没有任何影响,因为解析会重新开始。将其归结为特殊的cmd 怪异。


这个其实可以用python for windows来复现,和msys/mingw/git/bash/glibc/etc无关...

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

【讨论】:

以上是关于用于 Windows 的 Git 中的 Bash:使用 CMD.exe /C 和 args 运行命令时的怪异的主要内容,如果未能解决你的问题,请参考以下文章

Windows下使用Git Bash提交代码到GitHub

如何使用适用于 Windows 的 Git 更改 Git Bash 中的目录?

通过 Windows 终端中的脚本设置 git bash 配色方案

无法修改git bash Windows快捷方式

无法修改 git bash Windows 快捷方式

Windows 终端中的 Git Bash 在单独的窗口中打开