用于 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
和添加 \&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 中)>
cmd "/c echo test"
相同。 Windows cmd 仅使用 "
引号,因此在运行时之间的某个位置,您的(在 bash 中)$
cmd '/c echo test'
将所有参数转换为 "/c echo test"
,这很高兴是 parses。
因为从 bash 的角度来看,第 2 次/第 3 次/第 4 次尝试都是相同的,它们都给出相同的响应。惊喜"
是由于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
怪异。
这个其实可以用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 更改 Git Bash 中的目录?