|
在有些地方遇到 call set 的用法,而且它还和变量延迟有所联系。下面将从简单的实例开始对其抽丝剥茧。
先看下面字符串截取的一段代码:
- @echo off
- set a=opqrstuvw
- set c=%a:~0,%
- echo %c%
- pause
复制代码
很显然,由于字符串截取没有给出第 2 个参数,所以没有任何值赋值给 c,因此在 echo 变量c 时会提示“ECHO 处于关闭状态”。如果 %a:~0,% 写成 %a:~0,2%,那么会输出 op 。
我们将上面代码再修改一下:
- @echo off
- set b=2
- set a=opqrstuvw
- set c=%a:~0,%b%%
- echo %c%
- pause
复制代码
在上面的代码中,我们希望等同于 %a:~0,2% 这种形式(希望系统先对 %b% 解析成 2),从而也输出 op 。但事与愿违,解析器并不是按照这个“从里到外”的顺序来解析的,最后结果输出的 b% 。为什么?
对于 %a:~0,%b%% 是这样解析的:
首先,要了解到,% 在批处理里,也是转义字符的一种,正如在 《 如何输出百分数》里指出的,要想输出百分号,那么应当写成双百分号的形式 %% ,这里实际上是一个百分号对另一个百分号进行了转义。现在回头看上面语句的解析顺序:
先对后面两个百分号 %%,解析成单个 %,于是变成: %a:~0,%b%。接着,从左到右开始解析,那么开始解析的是 %a:~0,% 这部分的内容,上面谈到过,这部分内容为空,因此最后输出的只能是后半部的 b%。可以将 %a:~0,% 改写成 %a:~0,2%,那么你会看到 opb% 这个输出,这样就得到了这个解析顺序的验证。
明白了这一点,我们如何将 set c=%a:~0,%b%% 这条语句进行修改,从而达成我们的意愿(也就是只输出 op)呢?
现在,我们将 set c=%a:~0,%b%% 这条语句修改为 set c=%%a:~0,%b%%%,也就是语句前后各添加一个分号,那么会输出什么?
输出结果为:%a:~0,2% 。
有点意思了,从上面的输出结果可以看到,%b% 这个变量被解析为 2,而整个式子的形式也符合字符串截取的形式。那么为什么不输出 op ,而只是该表达式呢?
在解释这个问题之前,我们先考虑一下 %%a:~0,%b%%% 这个式子的解析顺序:
前面两个 %%,被解析成了一个 %,后面两个百分号也被解析成了一个 %,%b% 被解析成 2。这里要注意,这 3 个解析是同时进行的,而不是先解析成 %a:~0,%b%%%,然后再变成 %a:~0,%b%%,接着又变成 %a:~0,%b% ,这样看上去又走回了老路,显然不符合输出结果。那至于为什么是上述的 3 个解析同时进行,答案是没为什么,解析器就是这样写的。
现在回过头来看如何解决输出结果是 %a:~0,2% ,而不是直接 op。造成这个问题的原因正是“变量延迟”。在遇到像上述两次解析的时候(一次将 %b% 解析成 2,一次将 %a:~0,2% 解析成 op),触发了预处理机制,所以需要用到延迟变量。方法是在 set 语句前面添加一个 call 命令,也就是写成:call set c=%%a:~0,%b%%% 。
这样做,结果就正确了。如果要问为什么,可以这么理解,因为 call 相当于子程序的调用,它会进到自己的一个执行空间,所以在预处理后还要计算出结果来。
如果不用 call,并且要利用延迟变量的正规做法,那么首先要添加 setlocal enabledelayedexpansion 语句,然后将 call set 语句改写成:set c=!a:~0,%b%! ,注意此时 a 前面不需要再用到百分号。
|
|