我在Windows上的git bash中使用以下git命令:
git log --format="%C(cyan)%cd%Creset %s" --date=short -5
显示提交日期(%cd
)和提交消息(%s
)。提交日期用彩色标记包裹:%C(cyan)
开始彩色输出,%Creset
停止彩色输出。
虽然它在git bash中工作得很好,但它在cmd
中工作得不好:%cd%
被Windows shell扩展到当前工作目录(相当于bash中的$PWD
)。
因此,当通过cmd
运行该命令时,我在第一列中看到的是当前工作目录,而不是提交日期!git bash:
2015-10-08 commit msg
2015-10-08 commit msg
2015-10-07 commit msg
2015-10-06 commit msg
2015-10-06 commit msg
cmd:
D:gitsomeFolderCreset commit msg
D:gitsomeFolderCreset commit msg
D:gitsomeFolderCreset commit msg
D:gitsomeFolderCreset commit msg
D:gitsomeFolderCreset commit msg
实际上,我自己从来没有直接使用过cmd
,我在编写nodejs(0.12)脚本时发现了这种行为,其中我有
require('child_process').execSync('git log --format=...', {stdio: 'inherit'})
在Windows上由使用cmd
的节点执行
一个简单的解决方法是引入一个空格来防止%cd%
被发现,即更改
git log --format="%C(cyan)%cd%Creset %s" --date=short -5
git log --format="%C(cyan)%cd %Creset%s" --date=short -5
然而,这引入了一个冗余空间(我在%s
之前删除了另一个空间,但它仍然是一个hack,需要人工干预)。
有没有办法防止Windows shell扩展?
我找到了关于使用%%
或^%
来逃避%
的信息,但它们不是这里的解决方案:
# produces superfluous ^ characters
git log --format="%C(cyan)^%cd^%Creset %s" --date=short -5
^2015-10-08^ commit msg
^2015-10-08^ commit msg
^2015-10-07^ commit msg
^2015-10-06^ commit msg
^2015-10-06^ commit msg
# seems the expansion is done at command parse time
git log --format="%C(cyan)%%cd%%Creset %s" --date=short -5
%D:gitsomeFolder commit msg
%D:gitsomeFolder commit msg
%D:gitsomeFolder commit msg
%D:gitsomeFolder commit msg
%D:gitsomeFolder commit msg
理想的解决方案应该与bash和cmd兼容,而不产生多余的字符,或者在javascript中使用转义函数来转义Windows的通用UNIX-y命令,以防止展开(如果可以创建这样的转义函数)。
提供一个替代MC ND的有用答案:
如果您真的需要让shell参与(这不太可能,因为您声明您希望该命令同时与Windows的cmd.exe
和 Bash一起工作),请考虑下面的解决方案;对于非shell 替代方案,绕过问题,请参阅底部的解决方案。
向MC ND致敬,因为它改进了我的原始方法,建议在%
实例周围加上双引号,而不是在 %
实例之间加上潜在的变量名,并建议对execFileSync
进行澄清。
"转义" %
字符。for cmd.exe
正如MC ND的回答所述,从技术上讲,你不能在Windows命令提示符中逃避%
(在批处理文件中,你可以使用%%
,但这在从其他环境(如Node.js)调用shell命令时不起作用,并且通常不能跨平台工作)。
然而,解决方法是在每个%
实例:
// Input shell command.
var shellCmd = 'git log --format="%C(cyan)%cd%Creset %s" --date=short -5'
// Place a double-quote on either end of each '%'
// This yields (not pretty, but it works):
// git log --format=""%"C(cyan)"%"cd"%"Creset "%"s" --date=short -5
var escapedShellCmd = shellCmd.replace(/%/g, '"%"')
// Should work on both Windows and Unix-like platforms:
console.log(require('child_process').execSync(escapedShellCmd).toString())
插入的双引号防止cmd.exe
将诸如%cd%
之类的标记识别为变量引用("%"cd"%"
不会被展开)。
这是有效的,因为在目标程序处理时,额外的双引号最终会从字符串中删除:
Windows:
git.exe
(可能是通过C运行时)然后负责从组合字符串中去掉额外的双引号。类unix (类posix shell,如Bash): shell自己会在将双引号传递给目标程序之前删除它们。
- 警告:在命令中使用双引号通常意味着您需要注意类似posix的shell对
$
前缀的令牌执行潜在的不必要的扩展(这里不是问题);但是,为了与windows兼容,必须使用双引号。
- 警告:在命令中使用双引号通常意味着您需要注意类似posix的shell对
从技术上讲,将此技术应用于双引号字符串将其分解为双引号子字符串序列,其中穿插着未引号 %
实例。类posix shell仍然将其识别为单个字符串-子字符串是双引号,并且直接关于%
实例。(如果将该技术应用于未加引号的字符串,则逻辑相反:您实际上是在双引号的%
实例中进行拼接。)子字符串周围的双引号被认为是语法元素,而不是字符串的一部分,当子字符串连接在一起形成传递给目标程序的单个字面值时,将被删除。
通过完全避免shell来绕过这个问题
注意:以下构建在execFile[Sync]
之上,它只适用于调用外部可执行文件(在OP的情况下:git.exe
) -相反,对于调用shell内置文件(内部命令)或Windows 批处理文件,您不能避免exec[Sync]
,因此由cmd.exe
(在Windows上)解释。[1]
如果使用execFileSync
而不是execSync
,则shell (Windows上的cmd.exe
)将不涉及,因此您不必担心转义%
字符。或任何其他shell元字符:
require('child_process').execFileSync('git',
[ 'log',
'--format=%C(cyan)%cd%Creset %s',
'--date=short',
'-5' ], {stdio: 'inherit'})
注意参数必须单独作为数组的元素,并且没有嵌入引号。
[1]在Windows上,脚本文件(例如,Python脚本)不能用 总之,execFile[Sync]
直接调用,但您可以将解释器可执行文件(例如,python
)作为要执行的文件,并将脚本文件作为参数传递。在类unix平台上,可以直接调用脚本,只要它们有shebang行并被标记为可执行。execFile[Sync]
可以用来调用Windows 批处理文件,但是cmd.exe
总是插入参数,就像exec[Sync]
一样。你不能这样做或者至少没有一个通用的方法
注释我错了。Mklement0的回答为一般解决方案指明了一条道路。
当然,您可以使用^
,而不是转义百分号(您不能在命令行级别转义百分号),而是将百分号与变量名分开,这样就不会将其检测为要展开的变量。也就是说,对于您的情况,使用
git log --format="%C(cyan)%cd^%Creset %s" --date=short -5
,但是当您指向时,插入符号不会被解析器使用,而是包含在输出字符串中。
因此,在cmd
和bash
中,任何"转义"字符都将包含在输出中,但是可以使用
解决这种特殊(或类似)情况。process.env.cd = '%cd%'
require('child_process').execSync(
'git log --format="%C(cyan)%cd%Creset %s" --date=short -5'
, {stdio: 'inherit'}
)
基本上,代码所做的是为cd
环境变量分配一个假值,将其更改为文字%cd%
,因此,当cmd
解析器执行变量展开时,正确的值将在命令行结束。
但是,也许(我没有检查git
代码如何/做什么)改变cd
变量可能会干扰。同样,在这种情况下,不需要改变cd
变量的值你可以使用
process.env['C(cyan)'] = '%C(cyan)%';
为什么?当cmd
解析器处理命令行时,它将根据现有变量匹配格式字符串的开头,进行展开,并在相同的过程中消耗%cd%
变量中的起始百分号,因此它不会展开。