为什么在Windows 7上无法访问名为__CD__的变量? [英] Why can't I access a variable named __CD__ on Windows 7?

查看:141
本文介绍了为什么在Windows 7上无法访问名为__CD__的变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意 - 这个问题是基于在Windows 7上观察到的行为。我相信该行为适用于Vista之后的所有其他版本。基于MC ND的回答和Foxidrive的评论,它不适用于XP。



我可以使用伪动态变量% CD%以获取当前目录,不带尾随 \ 。如果我使用设置CD = someValue定义一个名为CD的真实变量,那么%CD%返回值I一个不太知名的动态变量是%__ CD __%,这是一样的,而不是当前目录。



作为%CD%,除了它包含尾随 \ 。但是如果我执行设置__CD __ = someValue,我无法访问我的分配值!我可以使用 set __CD __ 来看到我的变量与我的分配值一起存在,但是%__ CD __%总是返回当前目录!



有没有人知道这个%__ CD __%机制是什么(为什么它不同于任何其他动态变量)?



有人可以使用一种方法来访问我的 __ CD __ 变量,只使用标准批处理命令(不解析的输出集__CD __ )?



这更像是一个学术问题,而不是一个实用的问题。没有人应该定义具有与动态变量匹配的名称的变量。






我不想要一个例程来解析 set __CD __ 的结果是因为值可以在其中包含换行符。有人可以定义两个变量: __ CD __ __ CD__2 ,或者可以定义一个 __ CD __ 值,其中包含换行符,后跟 __ CD__2 = ... 。区分这两种情况是不可能的。 (正如我所说,这是一个学术问题,而不是一个实际的问题!)

解决方案

我有一个关于如何和为什么 __ CD __ 的行为与任何其他伪环境变量的行为截然不同。



我写了一个简单的ENV.JS脚本探讨流程环境。

  var env = WScript.CreateObject(WScript.Shell)。 ; 
WScript.echo(env(WScript.Arguments.Item(0)));

ENV.JS期望一个环境变量名称作为唯一的参数,只需打印变量。我在Windows 7机器上进行了测试。该脚本可以在CMD.EXE控制台中运行,也可以通过定义参数的快捷方式运行。



我将对各种动态变量进行分类,显示对比行为,以及关于它们工作机制的理论。



有许多动态变量可以分为三个类:



1)正常的变量没有任何前缀或后缀

 code> CD当前目录
DATE当前日期
TIME当前时间
ERRORLEVEL当前错误级别
RANDOM随机整数介于0和32767之间
CMDEXTVERSION当前扩展级别
CMDCMDLINE调用当前CMD.EXE级别的命令行

动态值仅通过CMD.EXE中的扩展可用,并且只有在命令扩展名为en时体健。它们不能通过SET或ENV.JS。可以通过使用SET显式定义静态值来覆盖动态值。覆盖值可通过SET和ENV.JS获得。

  C:\test> echo%cmdcmdline%
C:\Windows\system32\cmd.exe

C:\test>设置cmdcmdline
环境变量cmdcmdline未定义

C: \test> cscript // nologo env.js cmdcmdline
cmdcmdline =

C:\test>设置cmdcmdline = override

C:\test> echo%cmdcmdline%
覆盖

C:\test>设置cmdcmdline
cmdcmdline = override

C:\test> cscript // nologo env.js cmdcmdline
cmdcmdline = override

C:\test> cmd / e:off
Microsoft Windows [Version 6.1.7601]
版权所有(c) 2009年微软公司。版权所有。

C:\test> echo%cmdcmdline%
%cmdcmdline%

动态值根本不是真正的环境变量,这就是为什么它们无法访问SET命令或ENV.JS。 CMD.EXE中的变量扩展代码首先检查变量的过程环境变量,并且只有在没有找到它将名称与特殊动态名称列表进行比较时。每个动态变量必须存在特殊的CMD.EXE代码,以从适当的源中导出值。



这个动态变量类的行为由微软的Raymond陈在他的博客 - 新旧事物



2)动态变量前缀为 =

  = ExitCode 
= ExitCodeAscii
= C:
= D:
...
= Z:

这些动态值中的每一个都是未定义的,直到执行定义它的某个命令。例如,全新的CMD.EXE控制台会话以= ExitCode未定义开头。执行一个设置返回码的外部命令后,它就被定义。



有一个例外是 = {driveLetter}:变量对应于当前目录将永远定义,即使CMD.EXE首次启动。



不可能使用SET定义任何这些变量,因为SET不允许变量名称中的 = 。但是,基础进程环境变量空间确实允许变量名中的 = 。它必须在CMD.EXE上下文之外完成。



我已经写了一个额外的TEST.JS来帮助测试这些变量:

  var shell = WScript.CreateObject(WScript.Shell); 
var env = shell.Environment(Process);
WScript.echo('JScript中:ExitCode ='+ env(= ExitCode));
env(= ExitCode)=override;
WScript.echo('在覆盖后的JScript中:ExitCode ='+ env(= ExitCode));
WScript.echo('JScript test.bat中的返回码='+ shell.run(cmd / c test.bat,10,1));
WScript.echo('JScript after test.bat:ExitCode ='+ env(= ExitCode));

反过来,TEST.JS调用TEST.BAT:

  @echo off 
echo在test.bat中:ExitCode =%= ExitCode%
cmd / c exit 10
echo =内测.bat:ExitCode =%= ExitCode%
pause
exit%errorlevel%

以下是使用 = ExitCode 的一些测试结果,从全新的CMD.EXE会话开始:

  Microsoft Windows [Version 6.1.7601] 
版权所有(c)2009 Microsoft Corporation。版权所有。

C:\test> echo%= ExitCode%
%= ExitCode%

C:\test> set= ExitCode = override
命令的语法不正确。

C:\test> cscript // nologo env.js = ExitCode
= ExitCode =

C:\test> REM cscript是一个外部命令设定价值!

C:\test> echo%= ExitCode%
00000000

C:\test> set =
命令的语法是不正确。

C:\test> set| findstr / b = ExitCode
= ExitCode = 00000000

C:\test> cscript // nologo env .js = ExitCode
= ExitCode = 00000000

C:\test> cscript // nologo test.js
在JScript中:ExitCode = 00000000
在JScript之后覆盖:ExitCode = override
在JScript中test.bat返回代码= 10
在test.bat之后的JScript中:ExitCode = override

C:\test>

以下是TEST.BAT在新窗口中启动的结果:


 在test.bat内:ExitCode = override 
在test.bat内:ExitCode = 0000000A
按任意键继续。 。 。

我相信这些动态变量是真正的环境变量,这就是为什么SET和JScript都可以访问它们。 (SET只能使用特殊的 SET语法访问该值。)每次执行relavent命令时,变量都由CMD.EXE动态定义(或更新)。 ENV.JS和TEST.JS可以看到由调用CMD.EXE会话设置的值。 TEST.BAT cmd会话可以看到TEST.JS设置的继承覆盖值。但是,由于CMD.EXE没有使用返回码来更新该值,所以JScript在TEST.BAT之后继续获得覆盖值。



这一类的动态变量是无论命令扩展是启用还是禁用,都可用。即使扩展被禁用,也会保持动态值,如下所示:

  C:\test> cmd / e:off 
Microsoft Windows [Version 6.1.7601]
版权所有(c)2009 Microsoft Corporation。版权所有。

C:\test> echo%= c:%
C:\test

C:\test> cd test

C:\test\test> echo%= c:%
C:\test\test

3)__CD__ - 一个特殊情况全部自己!

编辑 - 其实__APPDIR__的工作原理与



此动态变量始终可用于CMD.EXE和JScript。可以定义覆盖值,但是除了SET命令可以列出覆盖值之外,CMD.EXE和JScript都不能看到覆盖值。 (另外,没有显示,但是发现SET / A可以读取覆盖值,如果它是数字)。



我写了另一个TEST2.JS来探测这个变量。

  var shell = WScript.CreateObject(WScript.Shell); 
var env = shell.Environment(Process);
WScript.echo('在JScript中:__CD __ ='+ env(__ CD__));
env(__ CD__)=JS覆盖;
WScript.echo('在覆盖后的JScript中:__CD __ ='+ env(__ CD__));
shell.run('cmd / c'set __CD __& pause,1,0');

以下是一些测试的结果:

  Microsoft Windows [Version 6.1.7601] 
版权所有(c)2009 Microsoft Corporation。版权所有。

C:\test> echo%__ CD __%
C:\test\

C:\test> set __CD__
环境变量__CD__未定义

C:\test>设置__CD __ =批量覆盖

C:\test> echo%__ CD __%
C:\\ \\ test\

C:\test> set __CD__
__CD __ =批量覆盖

C:\test> cscript // nologo test2.js
在JScript中:__CD __ = C:\test\
覆盖后的JScript中:__CD __ = C:\test\

C:\test>

以下是TEST2.JS打开的CMD.EXE窗口的结果:

  __ CD __ = JS覆盖
按任意键继续。 。 。

如果我将ENV.JS的快捷方式定义如下:

 目标:C:\test\env.js __CD__ 
开始于:C:\test\xyz

然后当我点击它时,我收到一个提示框,说明:

  __ CD __ = C:\test\xyz\ 



__ CD __ 的值就自动返回进程的当前目录。这样做,即使定义了一个名为 __ CD __ 的真实静态环境变量。



CMD.EXE SET命令必须访问环境变量与大多数其他上下文不同。我想象一个C程序可以被写入,以获得指向进程环境内存的指针,并解析任何真正的用户定义的 __ CD __ 值,就像SET命令一样。



鉴于这是提供此值的低操作系统例程,即使在命令中,%__ CD __%也不奇怪扩展名被禁用。

  C:\test> cmd / e:off 
Microsoft Windows [Version 6.1.7601 ]
版权所有(c)2009 Microsoft Corporation。版权所有。

C:\test> echo%__ cd __%
C:\test\

C:\test> cd test

C:\test\test> echo%__ cd __%
C:\test\test\

我认为当前目录的概念对于每个进程来说都是至关重要的,而且MS决定通过一个动态的虚拟环境变量给进程访问该值。 XP操作系统允许扩展任何用户定义的 __ CD __ 覆盖值。也许这会导致某些应用程序出现问题,MS决定修改环境访问例程(从Vista开始?)始终返回真实的当前目录,无论用户定义的任何<_ c $ c> __ CD __ 变量



所以,基于这个理论,它是返回当前目录的一个低级例程,我现在认为不可能使用本地批处理命令可靠地获取任何用户定义的值 __ CD __


Note - this question is based on behavior observed on Windows 7. I believe the behavior applies to all other versions from Vista onward. Based on MC ND's answer, and Foxidrive's comments, it does not apply to XP.

I can use the pseudo dynamic variable %CD% to get the current directory, without a trailing \. If I define a true variable named CD using set "CD=someValue", then %CD% returns the value I assigned, and not the current directory.

A lesser known dynamic variable is %__CD__%, which is the same as %CD%, except it includes the trailing \. But if I do set "__CD__=someValue", I cannot access my assigned value! I can use set __CD__ to see that my variable exists with my assigned value, but %__CD__% always returns the current directory!

Does anyone have any idea how this %__CD__% mechanism works (why it is different than any other dynamic variable)?

Can someone come up with a way to access my __CD__ variable using nothing but standard batch commands (without parsing the output of set __CD__)?

This is more an academic question, not a practical one. No one should be defining variables with names that match dynamic variables.


The reason I do not want a routine that parses the results of set __CD__ is because values can have line feeds in them. Someone could define two variables, __CD__ and __CD__2, or they could define a single __CD__ value with a containing a line feed followed by __CD__2=.... It would be impossible to differentiate between those two scenarios. (As I said, this is an academic question, not a practical one!)

解决方案

I have a theory as to how and why __CD__ behaves so differently than any of the other pseudo environment variable.

I wrote a simple ENV.JS script to probe the process environment.

var env=WScript.CreateObject("WScript.Shell").Environment("Process");
WScript.echo(env(WScript.Arguments.Item(0)));

ENV.JS expects an environment variable name as the one and only argument, and simply prints the value of the variable. I did my testing on a Windows 7 machine. The script can be run from within a CMD.EXE console, or it can be run directly via a shortcut that defines the argument.

I will classify the various dynamic variables, showing contrasting behaviors, along with a theory as to the mechanism by which they work.

There are a number of dynamic variables that can be divided into three classes:

1) Normal looking "variables" without any prefix or suffix

CD              current directory
DATE            current date
TIME            current time
ERRORLEVEL      current errorlevel
RANDOM          random integer between 0 and 32767
CMDEXTVERSION   current extension level (only available if extensions are enabled)
CMDCMDLINE      the command line that invoked the current CMD.EXE level

The dynamic values are only available via expansion within CMD.EXE, and only when command extensions are enabled. They are not available via SET, or by ENV.JS. The dynamic values can be overridden by explicitly defining a static value using SET. The override values are available via SET and ENV.JS.

C:\test>echo %cmdcmdline%
"C:\Windows\system32\cmd.exe"

C:\test>set cmdcmdline
Environment variable cmdcmdline not defined

C:\test>cscript //nologo env.js cmdcmdline
cmdcmdline=

C:\test>set cmdcmdline=override

C:\test>echo %cmdcmdline%
override

C:\test>set cmdcmdline
cmdcmdline=override

C:\test>cscript //nologo env.js cmdcmdline
cmdcmdline=override

C:\test>cmd /e:off
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\test>echo %cmdcmdline%
%cmdcmdline%

The dynamic values are not really environment variables at all, which is why they are not accessible to the SET command or to ENV.JS. The variable expansion code in CMD.EXE first checks the process environment variables for the variable, and only if not found does it compare the name against the list of special dynamic names. Special CMD.EXE code must exist for each dynamic "variable" to derive the value from the appropriate source.

The behavior of this class of dynamic variables is described by Microsoft's Raymond Chen within his blog - The Old New Thing

2) Dynamic variables prefixed with =

=ExitCode
=ExitCodeAscii
=C:
=D:
...
=Z:

Each of these dynamic values is undefined until some command is executed that defines it. For example, a brand new CMD.EXE console session starts with =ExitCode undefined. It becomes defined once an external command is executed that set a return code.

There is one exception in that the ={driveLetter}: variable corresponding to the current directory will always be defined, even when CMD.EXE first starts up.

It is impossible to use SET to define any of these variables because SET does not allow = in a variable name. But the underlying process environment variable space does allow = in variable names. It just must be done outside of the CMD.EXE context.

I've written an additional TEST.JS to help test these variables:

var shell=WScript.CreateObject("WScript.Shell");
var env=shell.Environment("Process");
WScript.echo('Within JScript: ExitCode='+env("=ExitCode"));
env("=ExitCode") = "override";
WScript.echo('Within JScript after override: ExitCode='+env("=ExitCode"));
WScript.echo('Within JScript test.bat return code = '+shell.run("cmd /c test.bat",10,1));
WScript.echo('Within JScript after test.bat: ExitCode='+env("=ExitCode"));

In turn, TEST.JS calls TEST.BAT:

@echo off
echo Within test.bat: ExitCode=%=ExitCode%
cmd /c exit 10
echo =Within test.bat: ExitCode=%=ExitCode%
pause
exit %errorlevel%

Here are some test results using =ExitCode, starting with a brand new CMD.EXE session:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\test>echo %=ExitCode%
%=ExitCode%

C:\test>set "=ExitCode=override"
The syntax of the command is incorrect.

C:\test>cscript //nologo env.js =ExitCode
=ExitCode=

C:\test>REM cscript is an external command that set the value!

C:\test>echo %=ExitCode%
00000000

C:\test>set =
The syntax of the command is incorrect.

C:\test>set ""|findstr /b =ExitCode
=ExitCode=00000000

C:\test>cscript //nologo env.js =ExitCode
=ExitCode=00000000

C:\test>cscript //nologo test.js
Within JScript: ExitCode=00000000
Within JScript after override: ExitCode=override
Within JScript test.bat return code = 10
Within JScript after test.bat: ExitCode=override

C:\test>

Here are the results of TEST.BAT that TEST.JS launched in a new Window:

Within test.bat: ExitCode=override
Within test.bat: ExitCode=0000000A
Press any key to continue . . .

I believe that these dynamic variables are true environment variables, which is why both SET and JScript can access them. (SET can only access the value using the special SET "" syntax.) The variables are dynamically defined (or updated) by CMD.EXE each time a relavent command is executed. ENV.JS and TEST.JS can see the value that was set by the calling CMD.EXE session. The TEST.BAT cmd session could see the inherited override value that TEST.JS set. But JScript continued to get the override value after TEST.BAT exited because CMD.EXE was not there to update the value with the return code.

This class of dynamic variables is available regardless whether command extensions are enabled or disabled. The dynamic values are maintained even when extensions are disabled, as evidenced below:

C:\test>cmd /e:off
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\test>echo %=c:%
C:\test

C:\test>cd test

C:\test\test>echo %=c:%
C:\test\test

3) __CD__ - A special case all unto itself!
EDIT - Actually, __APPDIR__ works the same way

This dynamic variable is always available to both CMD.EXE and JScript. An override value can be defined, but neither CMD.EXE nor JScript can see the override value, except the SET command can list the override value. (Also, not shown, but jeb discovered SET /A can read the override value if it is numeric).

I wrote yet another TEST2.JS to probe this variable.

var shell=WScript.CreateObject("WScript.Shell");
var env=shell.Environment("Process");
WScript.echo('Within JScript: __CD__='+env("__CD__"));
env("__CD__") = "JS override";
WScript.echo('Within JScript after override: __CD__='+env("__CD__"));
shell.run('cmd /c "set __CD__&pause",1,0');

Here are the results of some tests:

Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\test>echo %__CD__%
C:\test\

C:\test>set __CD__
Environment variable __CD__ not defined

C:\test>set "__CD__=Batch override"

C:\test>echo %__CD__%
C:\test\

C:\test>set __CD__
__CD__=Batch override

C:\test>cscript //nologo test2.js
Within JScript: __CD__=C:\test\
Within JScript after override: __CD__=C:\test\

C:\test>

Here is the result of the CMD.EXE window that TEST2.JS opened up:

__CD__=JS override
Press any key to continue . . .

If I define a shortcut to ENV.JS as follows:

Target:   C:\test\env.js __CD__
Start in: C:\test\xyz

Then when I click on it, I get an alert box stating:

__CD__=C:\test\xyz\

I find these results fascinating. The dynamic value must not be a true environment variable. Presumably there is a low level OS environment variable access routine that automatically returns the process's current directory whenever it is asked to return the value of __CD__. It does so, even if a true static environment variable named __CD__ is defined.

The CMD.EXE SET command must access the environment variable differently than most other contexts. I imagine a C program could be written to get a pointer to the process environment memory and parse any true user defined __CD__ value, much as the SET command.

Given that it is a low operating system routine that provides this value, it is not surprising that %__CD__% is available even when command extensions are disabled.

C:\test>cmd /e:off
Microsoft Windows [Version 6.1.7601]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\test>echo %__cd__%
C:\test\

C:\test>cd test

C:\test\test>echo %__cd__%
C:\test\test\

I suppose the concept of a current directory is critical for every process from an OS perspective, and MS decided to give a process access to the value via a dynamic virtual environment variable. The XP OS allows expansion of any user defined __CD__ override value. Perhaps that caused problems for some applications, and MS decided to modify the environment access routines (starting with Vista perhaps?) to always return the true current directory, regardless of any user defined __CD__ variable.

So, based on the theory that it is a low level routine that is returning the current directory, I now believe it is impossible to use native batch commands to reliably get any user defined value for __CD__.

这篇关于为什么在Windows 7上无法访问名为__CD__的变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆