Windows 批处理中的字符串替换

Windows 批处理中的字符串替换

我试图了解 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% 内,您就会遇到问题。所以这可能是人们不完全打开该模式的原因,可能还有其他原因。

相关内容