有没有办法防止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

但正如您所指出的那样,插入符号不会被插入符号消耗掉并包含在输出字符串中。

因此,任何“转义”字符都将包含在cmdbash的输出中,但它可以使用此特定(或类似)情况解决

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 文件中读取变量

使用 `env` 命令和通过文件传递环境变量的最佳方式

收藏的一些github开源项目,在这里记录一下

有没有办法对用户在 Python 命令行中输入的文本进行哈希处理? [复制]

Houdini Mac 添加external editor