【问题标题】:How windows cmd set parse variable names?windows cmd set如何解析变量名?
【发布时间】:2021-02-11 14:51:29
【问题描述】:

如果我运行set 如下:

C:\Users\vagrant>SET UNDEFINED=foo

C:\Users\vagrant>SET UNDEFINED
UNDEFINED=foo

C:\Users\vagrant>SET UNDEFINED  
Environment variable UNDEFINED   not defined

C:\Users\vagrant>SET UNDEFINED | more
UNDEFINED=foo

C:\Users\vagrant>SET UNDEFINED >nul

C:\Users\vagrant>SET UNDEFINED >nul 
Environment variable UNDEFINED   not defined

C:\Users\vagrant>SET UNDEFINED  | more
Environment variable UNDEFINED   not defined

C:\Users\vagrant>SET UNDEFINED >nul | more
Environment variable UNDEFINED   not defined

C:\Users\vagrant>SET UNDEFINED >nul| more

C:\Users\vagrant>SET UNDEFINED 2>nul | more

C:\Users\vagrant>SET UNDEFINED 2>nul| more
UNDEFINED=foo

注意,上面的第二条命令是SET UNDEFINED ,后面有两个空格。并且SET UNDEFINED >nul SET UNDEFINED >nul | moreSET UNDEFINED 2>nul | more| 之前多了一个空格。在这些命令中,set 用另外两个空格解析变量。那么set 是如何解析变量名的。我也找到了cmd parse scripts,但是这里变量名是怎么分词的?

编辑 当空格使用预文件重定向时会出现此问题。换句话说,在字符串后添加空格并重定向。例子: echo foo>bar.txt bar.txt 前面的空格以及 foo 附加到文件中。

以下是这方面的示例:

例子:

结果:

【问题讨论】:

  • 当您添加额外的空格时,您实际上是在告诉 set 显示一个名为“UNDEFINED”` 或 %UNDEFINED % 实际上未定义的变量的值。您可以看到,如果您使用双引号后的空格执行set "UNDEFINED" ,空格将不再是问题。这与执行set UNDEFINED =foo 相同,它不会为%UNDEFINED% bu 返回一个变量,而是为%UNDEFINED % 返回一个变量
  • 请在Why is no string output with 'echo %var%' after using 'set var = text' on command line? 上阅读我的答案 简单的解决方案是set "UNDEFINED" 以获取输出环境变量列表,其名称以UNDEFINED 开头,不区分大小写,独立于后面的空格命令行。
  • @Mofi。谢谢。引用变量是避免问题的好方法。但困扰我的是为什么命令SET UNDEFINED >nul | more的输出很奇怪。
  • @zhenguoli,正如我在下面的答案中提到的那样。请参阅我对答案所做的编辑。
  • @zhenguoli 我在this answer 中详细解释了在使用echoset 之类的命令时留给>nul 的空格会发生什么,这些命令不会将空格解释为参数分隔符。这可以在仅使用两行 set UNDEFINED=fooset UNDEFINED >nul | more 创建批处理文件并从命令提示符窗口中执行此批处理文件时看到。真正执行的第二个命令行是set UNDEFINED   1>nul  | more。请注意cmd.exe 插入的额外空格。一种解决方案是set "UNDEFINED" >nul | more

标签: windows batch-file cmd environment-variables


【解决方案1】:

当您添加额外的空格时,您实际上是在告诉 set 显示名为 "UNDEFINED "%UNDEFINED % 的变量的值,这实际上是未定义的。您可以看到,如果您使用双引号后的空格进行set "UNDEFINED" ,则空格将不再是问题。这与执行set UNDEFINED =foo 相同,它不会为%UNDEFINED% bu 返回一个变量,而是为%UNDEFINED % 返回一个变量

以下是这方面的示例:

set UNDEFINED=foo
set UNDEFINED =foo

然后运行时,没有任何额外的空格:

set UNDEFINED 结果符合预期。

UNDEFINED=foo
UNDEFINED =foo

但是当你用额外的空格运行它时 set UNDEFINED 由于您提供了额外的空格,结果不再匹配 %UNDEFINED%

UNDEFINED =foo

在这里我们可以更多地展示匹配的工作原理

set UNDEF=foo
set UNDEFI=foo
set UNDEFIN=foo
set UNDEFINE=foo
set UNDEFINED=foo
set UNDEFINED =foo
set UNDEFINED  =foo
set UNDEFINED   =foo

现在看看所有这些的结果

set UN
set UNDEF
set UNDEFINE
set UNDEFINED

显然是通过添加空格: set UNDEFINED

但是如果我们添加 4 个空格,代表一个我们从未设置过的变量,那么我们将得到 un undefined 变量结果。 set UNDEFINED

最后,要克服这个问题,要确保我们正确设置变量并将它们双引号以获得所需的结果。

set "UNDEFINED=foo"
set "UNDEFINED"

后面的 set 命令不会关心你在双引号结束后给出了多少空格,它会返回任何包含单词UNDEFINED 的变量。即set "UNDEFINED"

使用重定向> 或管道| 的结果也是如此。具体使用重定向。 > 之前的所有内容都被视为您要重定向的字符串。示例:

echo foo >out.txt

将导致 out.txt 包含 foo 包括空格。 因此,需要排除空格,但这会成为一个问题,如果您的字符串以数字结尾。即

echo foo2>out.txt

这会将stderr 重定向到文件。 所以我们再次克服了这个问题,将代码括起来:

(echo foo)>out.txt

因此,根据您的示例,尽管管道到nul 不会返回任何结果,除非您指定类型(stdout/stderr)

(SET UNDEFINED)2>nul|more
(SET UNDEFINED)1>nul|more
(SET UNDEFINED)>nul|more

编辑

再次。带空格的字符串前面的附加空格将在输出中添加空格。再次使用echo

(echo foo | more)>out.txt

foo| 之间有一个空格,如果您查看out.txt 中的结果,您会注意到该空格。这同样适用于重定向>,因此在这些实例中使用带括号的块来消除空格。

EDIT2

根据您提供的屏幕截图。 cmd 使用重定向的方式并不真正关心您将其放置在哪里。

>out.txt echo foo

将导致:

foo

在文件中。在哪里添加空格,将被追加到文件中

>out.txt echo foo 将导致foo

与您的示例类似,它将回显您在重定向行中提供的任何内容。

echo foo>out.txt

echo foo>out.txt . 将附加到文件中,因为重定向确实是此处的关键功能,它将附加您提供的内容。

但是,您会注意到重定向本身,然后在命令之前提供额外的空格将不会执行此操作。这仅仅是因为您要求系统将字符串重定向到文件,然后空格成为命令分隔符。所以这个:

>out.txt echo foo 只会在您的输出文件中产生foo

所以.. 确保我们不添加这些不需要的空格的最佳方法是: >out.txt(echo foo)

【讨论】:

  • 谢谢。但是对于命令SET UNDEFINED >nul | more。名称以两个空格结尾。好奇怪,| 运算符前面的空格好像是在名字UNDEFINED 后面追加的一样。
  • echo 可能是一个不以空格作为分隔符的特殊命令。 (我不确定。)但是对于SET UNDEFINED >nul (以空格结尾),它将看到变量名称UNDEFINED (以两个空格结尾)。所以结束空格被附加到变量名。我想知道为什么在名称后面附加了结尾空格。
  • 因为空格都作为字符串的一部分传递给重定向或管道。因此,当给出 2 时,将添加 2 个额外的空格。因此括号和没有空格(SET UNDEFINED)>nul|more
  • 我可能知道你的意思。 echo UNDEFINED>out.txt 会将UNDEFINED 写入out.txt,尽管命令行末尾还有两个空格。但行为很奇怪。 Linux shell 永远不会这样工作。
【解决方案2】:

语法set VAR 似乎只允许一个尾随SPACE(尽管当VAR 未定义时,SPACE 包含在错误消息中:@987654325 @)。使用引号时也是如此,因此set "VAR"set "VAR " 都返回VAR 的值。

更奇怪的是,尾随 SPACE 后面的附加字符,如 set VAR Xset "VAR X",仍然返回变量值。

这是由set命令对命令行的具体处理和解析造成的。我无法回答“为什么”这个问题,因为我不是cmd.exe 的开发者之一。


使用重定向时(如在set VAR > con 中),命令解释器会在某一时刻(在How does the Windows Command Interpreter (CMD.EXE) parse scripts? 中所述的阶段2 期间)删除重定向表达式(> con),独立于命令,留下剩余的命令行,包括潜在的 SPACEs。所以set VAR > con然后变成set VAR > con+SPACE,它在定义的时候返回VAR的值,而set VAR > con+SPACE变成set VAR+ SPACE + SPACE,返回 VAR 的值失败。 echo text> con + SPACEs 还保留了重定向部分后面的尾随 SPACEs,然后将其回显。

对于set,使用set "VAR" > con 之类的引号并不关心有多少尾随SPACEs,因此可以按预期工作。对于echo,一个选项是使用像(echo text) > con 这样的括号,另一个是像> con echo text 一样将重定向部分移到前面(但显然要避免尾随SPACEs)。

看看我的这个密切相关的问答主题:Conditional execution behind set VAR 2> nul fails

【讨论】:

  • 不,你指的是关于:Labels;需要参考语句»如果有特殊字符&|<>,[…]«»在@987654353的情况下@、<<>>> 重定向,重定向子句被解析,暂时删除,[…]«(在第 2 阶段),还有第 5.5 阶段(执行重定向)...
  • 谢谢。问题来自检查变量是否使用命令SET VAR 2>nul | findstr /I "." 定义,但它失败了。解析过程对我来说有点难以理解,我可能需要一些时间来深入了解它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-29
  • 1970-01-01
  • 2017-12-15
  • 2011-11-27
相关资源
最近更新 更多