有没有办法防止Windows命令行中env变量的百分比扩展?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有没有办法防止Windows命令行中env变量的百分比扩展?相关的知识,希望对你有一定的参考价值。
我在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
之前删除了另一个空格,但它仍然是一个黑客,需要手动干预)。
有没有办法防止Windows shell扩展?
我有found关于使用%%
或^%
来逃避%
的信息,但它们不是这里的解决方案:
# 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's helpful answer的替代方案:
如果你真的需要涉及shell(这不太可能,因为你声明你希望命令同时使用Windows的cmd.exe
和Bash),请考虑下面的解决方案;对于绕过问题的无壳替代方案,请参阅底部的解决方案。
MC ND通过建议在%
实例周围放置双引号而不是%
实例之间的潜在变量名称以及建议澄清re execFileSync
来改进我的原始方法。
"Escaping" %
chars. for cmd.exe
正如在MC ND's answer中所述,你无法在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在
$
-prefixed tokens上执行可能不需要的扩展(这里不是问题);但是,为了保持与Windows兼容,您必须使用双引号。
从技术上讲,将这种技术应用于双引号字符串将其分解为一系列双引号子串,其间插入了不带引号的%
实例。类似POSIX的shell仍然将其识别为单个字符串 - 子字符串是双引号并直接邻接%
实例。 (如果将该技术应用于不带引号的字符串,则逻辑相反:您在双引号%
实例中进行有效拼接。)子字符串周围的双引号(被认为是语法元素而不是字符串的一部分)是然后当子串连接在一起形成单个文字传递给目标程序时删除。
Bypassing the problem by avoiding the shell altogether
注意:以下构建在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上,无法使用execFile[Sync]
直接调用脚本文件(例如,Python脚本),但您可以将解释器可执行文件(例如,python
)作为要执行的文件传递,并将脚本文件作为参数传递。在类Unix的平台上,你可以直接调用脚本,只要它们有shebang line并被标记为可执行文件。
execFile[Sync]
可用于调用Windows批处理文件,但cmd.exe
总是插入参数,与exec[Sync]
一样。
简而言之, 你不能这样做,或者至少没有一般的方法
注意我错了。 mklement0's answer为一般解决方案指明了方向。
当然,你可以使用^
,不要逃避百分号(你无法逃脱命令行级别的百分号),而是将百分号与变量名称分开,这样它就不会被检测为要扩展的变量。也就是说,对于您的情况,它足以使用
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%
变量中的起始百分号,因此不会展开。
以上是关于有没有办法防止Windows命令行中env变量的百分比扩展?的主要内容,如果未能解决你的问题,请参考以下文章
Windows如何在cmd命令行中查看、修改、删除与添加、设置环境变量?
Dockerfile:有没有办法从 .env 文件中读取变量