jak*_*b.g 4 windows cmd variable-expansion node.js
我在 Windows 上的 git bash 中使用以下 git 命令:
git log --format="%C(cyan)%cd%Creset %s" --date=short -5
Run Code Online (Sandbox Code Playgroud)
它显示提交日期 ( %cd),后跟提交消息 ( %s)。提交日期用颜色标记包裹:%C(cyan)开始彩色输出和%Creset停止彩色输出。
虽然它在 git bash 中运行良好,但它不适用于cmd:%cd%被 Windows shell 扩展到当前工作目录(相当于$PWD在 bash 中)。
因此,当该命令通过 运行时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
Run Code Online (Sandbox Code Playgroud)
指令:
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
D:\git\someFolderCreset commit msg
Run Code Online (Sandbox Code Playgroud)
实际上,我cmd自己从来没有直接使用过,我在编写 nodejs (0.12) 脚本时发现了这种行为,其中有
require('child_process').execSync('git log --format=...', {stdio: 'inherit'})
Run Code Online (Sandbox Code Playgroud)
它由节点使用cmdWindows 上的when执行)。
一个简单的解决方法可能是引入一个空间以防止%cd%被发现,即更改
git log --format="%C(cyan)%cd%Creset %s" --date=short -5
Run Code Online (Sandbox Code Playgroud)
到
git log --format="%C(cyan)%cd %Creset%s" --date=short -5
Run Code Online (Sandbox Code Playgroud)
然而,这引入了一个冗余空间(我之前删除了另一个空间,%s但它仍然是一个黑客,需要手动干预)。
有没有办法防止 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:\git\someFolder commit msg
%D:\git\someFolder commit msg
%D:\git\someFolder commit msg
%D:\git\someFolder commit msg
%D:\git\someFolder commit msg
Run Code Online (Sandbox Code Playgroud)
理想的解决方案应该与 bash 和 cmd 兼容,不会产生多余的字符,或者在 javascript 中使用转义函数来转义 Windows 的通用 UNIX-y 命令以防止扩展(如果可以创建这样的转义函数)。
提供MC ND 有用答案的替代方案:
如果您真的需要让shell参与其中(这不太可能,因为您声明希望该命令同时适用于 Windowscmd.exe 和Bash),请立即考虑下面的解决方案;有关绕过该问题的无壳替代方案,请参阅底部的解决方案。
向MC ND 致敬,通过建议在%实例周围放置双引号而不是实例之间 的潜在变量名称来改进我的原始方法%,并建议澄清 re execFileSync。
%字符。为了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())
Run Code Online (Sandbox Code Playgroud)
插入的双引号阻止cmd.exe识别诸如%cd%变量引用之类的标记("%"cd"%"不会被扩展)。
这是有效的,因为在目标程序处理时,额外的双引号最终会从字符串中删除:
Windows :(git.exe大概是通过 C 运行时)然后负责从组合字符串中去除额外的双引号。
类 Unix(类POSIX 外壳,例如 Bash):外壳本身负责在将双引号传递给目标程序之前删除双引号。
$-prefixed 标记上执行可能不需要的扩展(这里不是问题);但是,为了保持与 Windows 兼容,您必须使用双引号。从技术上讲,将此技术应用于双引号字符串将其分解为一系列双引号子字符串,其中散布着未引用的 %实例。类似于 POSIX 的 shell 仍然将其识别为单个字符串 - 子字符串被双引号括起来并直接与%实例相邻。(如果您将该技术应用于未加引号的字符串,则逻辑相反:您实际上是在双引号%实例中进行拼接。)子字符串周围的双引号被视为语法元素而不是字符串的一部分,然后当子字符串连接在一起形成单个文字以传递给目标程序时删除。
注意:以下内容基于execFile[Sync],它仅适用于调用外部可执行文件(在 OP 的情况下为真:)git.exe - 相比之下,对于调用shell 内置命令(内部命令)或 Windows批处理文件,您无法避免exec[Sync]并因此解释由cmd.exe(on视窗)。[1]
如果您使用execFileSync而不是execSync,则不会涉及外壳(cmd.exe在 Windows 上),因此您不必担心转义%字符。或任何其他 shell 元字符,就此而言:
require('child_process').execFileSync('git',
[ 'log',
'--format=%C(cyan)%cd%Creset %s',
'--date=short',
'-5' ], {stdio: 'inherit'})
Run Code Online (Sandbox Code Playgroud)
注意如何的参数,必须提供单独作为一个元素阵列,并且没有嵌入引用。
[1] 在 Windows 上,不能直接使用 调用脚本文件(例如 Python 脚本)execFile[Sync],但您可以将解释器可执行文件(例如python)作为要执行的文件传递,并将脚本文件作为参数传递。在类 Unix 平台上,您可以直接调用脚本,只要它们有一个shebang 行并且被标记为可执行文件。
execFile[Sync] 可用于调用 Windows批处理文件,但cmd.exe总是插入参数,就像exec[Sync].