我试图了解 Windows 批处理中的字符串替换实际上是如何工作的,但遇到了麻烦。
@echo off
set var=wild
set varnew=%var:l=n%
echo var is: %var%
echo varnew is: %varnew%
确实有效;它生成预期的输出:
var is: wild
varnew is: wind
但这不行(例如目录“Main”):
@echo off
for /D %%G IN (*) do (
setlocal
echo G is: %%G
set _srcp=%%G
echo _srcp is %_srcp%
rem set _newp=%_newp:ai=_01_% <-- confused variable
set _newp=%_srcp:ai=_01_%
echo._newp is: %_newp%
endlocal
)
它生成以下输出:
G is: Main
_srcp is Main
_newp is: %_srcp:ai=_01_
我希望代码生成为_newp is: M_01_n
最后一行。我真的没有主意了,有人能给我指出正确的方向吗?
BB
答案1
你有几个问题:
%var%
扩展发生在解析语句时,在执行任何命令之前,一次性解析整个括号内的代码块。因此,该值是循环开始之前存在的值。解决方案是延迟扩展,它在执行循环中的每个命令时发生。你的逻辑是错误的 - _newp 的赋值应该基于 _srcp 的值
CMD 处理器是一个复杂的野兽(并且文档记录不全)。有多个点扩展了各种类型的变量,如果您真的想充分利用批处理编程,就必须完全理解它们。链接中解释了所有内容,但总而言之,扩展的顺序是:
1)%扩展-参数:echo %1
或者环境变量:echo %var%
---- 目前大部分解析已完成 ----
2) FOR 变量扩展:for %%A in (*) do echo %%A
3) 延迟环境变量扩展:echo !var!
4) CALL % 扩展 - 参数:call echo %%1
或者环境变量:call echo %%var%%
5)SET /A 环境变量扩展:`set /a "value=var+1"
请注意,延迟扩展需要通过以下方式启用延迟扩展SETLOCAL EnableDelayedExpansion
以下使用延迟扩展的代码将给出您所寻求的结果:
@echo off
for /D %%G in (*) do (
setlocal enableDelayedExpansion
echo G is: %%G
set "_srcp=%%G"
echo _srcp is !_srcp!
set "_newp=!_srcp:ai=_01_!"
echo _newp is: !_newp!
endlocal
)
%%G
注意,延迟扩展发生在 FOR 变量扩展之后,因此如果包含,结果将被破坏!
。这可以通过额外的 SETLOCAL 来避免:
for /D %%G in (*) do (
setlocal disableDelayedExpansion
echo G is: %%G
set "_srcp=%%G"
setlocal enableDelayedExpansion
echo _srcp is !_srcp!
set "_newp=!_srcp:ai=_01_!"
echo _newp is: !_newp!
endlocal
endlocal
)
您也可以使用带双百分比的 CALL 来获得所需的结果,但这样做会慢得多。如果执行几次,速度并不重要,但如果在循环中执行数千次,速度就会变得非常重要。
@echo off
for /D %%G in (*) do (
setlocal
echo G is: %%G
set "_srcp=%%G"
call echo _srcp is %%_srcp%%
call set "_newp=%%_srcp:ai=_01_%%"
call echo _newp is: %%_newp%%
endlocal
)
答案2
进一步了解 davebenham 的回答
在 FOR 或 IF 等块中执行 echo %var% 无法正常工作。%var% 变量不会更新。您必须使用 !var!,并且要使 !var! 正常工作,您必须设置本地 EnableDelayedExpansion
cmd 帮助中有对此的解释,但不清楚哪个命令的帮助解释了这一点!set /?
set /?
最后,添加了对延迟环境变量扩展的支持。默认情况下始终禁用此支持,但可以通过 CMD.EXE 的 /V 命令行开关启用/禁用。请参阅 CMD /?
延迟环境变量扩展对于解决当前扩展的限制很有用,这种限制发生在读取一行文本时,而不是执行时。以下示例演示了立即变量扩展的问题:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
永远不会显示该消息,因为在读取第一个 IF 语句时,两个 IF 语句中的 %VAR% 都会被替换,因为它在逻辑上包含 IF 的主体,而 IF 是一个复合语句。因此,复合语句中的 IF 实际上是在比较“之前”和“之后”,而这两者永远不会相等。同样,以下示例也不会按预期工作:
set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
因为它不会在当前目录中建立文件列表,而只会将 LIST 变量设置为找到的最后一个文件。同样,这是因为在读取 FOR 语句时 %LIST% 只展开一次,而此时 LIST 变量为空。因此,我们执行的实际 FOR 循环是:
for %i in (*) do set LIST= %i
它只是不断将 LIST 设置为最后找到的文件。
延迟环境变量扩展允许您使用不同的字符(感叹号)在执行时扩展环境变量。如果启用了延迟变量扩展,则上述示例可以按如下方式编写以按预期工作:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "!VAR!" == "after" @echo If you see this, it worked ) set LIST= for %i in (*) do set LIST=!LIST! %i echo %LIST%
您还可以使用命令行测试符号
cmd /e:on
从默认的 cmd /v:off 开始,然后转到 cmd/v:on 大多数人甚至专家都使用 cmd .v:off(可能是因为其他东西可以用它进行不同的解释),所以我只是用这个来向你展示你可以尝试在 cmd 中使用 !var! 符号。
C:\>set a=5
C:\>echo %a%
5
C:\>echo !a!
!a!
C:\>cmd /v:on
Microsoft Wind
Copyright (c)
C:\>echo !a!
5
C:\>
顺便说一句,如果您有 cmd /v:on 或在批处理文件中启用了 EnableDelayedExpansion,那么正如 Dave 所示,您必须意识到 ! 是一个特殊字符,因此如果 ! 在 %var% 内,您就会遇到问题。所以这可能是人们不完全打开该模式的原因,可能还有其他原因。