如何修改循环内的环境变量值?

时间:2018-04-13 21:46:11

标签: batch-file

我在批处理文件中有以下代码:

set "start=2"

FOR /L %%a in (1,1,3) DO (
    FOR /L %%b in (%start%,1,3) DO (
        echo %start% Before Call
        call :changestring
        FOR /L %%c in (1,1,3) DO (
            REM Nothing
        )
    )
)

pause
Goto :Eof

:changestring
echo %start% Before Set
set "start=1"
echo %start% After Set
pause
goto :eof

我想要的是变量start的值在第二个 FOR 循环中引用两次,%start%在运行后从2更改为1子程序changestring,命令行为call :changestring

环境变量start的值在子例程中从2修改为1,作为子例程中两个echo命令行的输出。但在第二个 FOR 循环中,该值始终为2且不会更改。

如何在第二个 FOR 循环中修改环境变量start的值?

1 个答案:

答案 0 :(得分:1)

在运行批处理文件时,首先执行pause,如同在命令提示符窗口中发布的那样,没有通常在批处理文件顶部使用的@echo off {{3} }}:

set "start=2"

FOR /L %a in (1 1 3) DO (FOR /L %b in (2 1 3) DO (
echo 2 Before Call
 call :changestring
 FOR /L %c in (1 1 3) DO (REM Nothing )
) )

(FOR /L %b in (2 1 3) DO (
echo 2 Before Call
 call :changestring
 FOR /L %c in (1 1 3) DO (REM Nothing )
) )

(
echo 2 Before Call
 call :changestring
 FOR /L %c in (1 1 3) DO (REM Nothing )
)
2 Before Call

echo 2 Before Set
2 Before Set

set "start=1"

echo 1 After Set
1 After Set

pause

Windows命令解释cmd.exe 解释批处理文件并执行命令行输出,而不执行echo off命令行,在执行预处理/解析之后始终执行命令行,如{ {3}}

可以看出,在执行最外面的 FOR 命令之前,Windows命令解释器完全预处理以(开头并以匹配)结尾的命令块时间。在通过引用的环境变量的当前值%start%预处理整个命令块期间,两个2的出现都被替换。因此, FOR 几次使用的整个命令块不再包含环境变量引用。只有循环变量引用保留在命令块中。

命令提示符窗口中运行set /?时命令 SET 输出的帮助描述了在预处理期间已经评估%variable%所做的所有环境变量引用的问题。 SET 的帮助还描述了 IF 条件和 FOR 循环的解决方案,其中环境变量的当前值在命令中发生变化阻止或在循环执行期间:使用debug this batch file

因此,让我们按照How does the Windows Command Interpreter (CMD.EXE) parse scripts?中的说明使用命令 SETLOCAL ENDLOCAL 来启用延迟环境变量扩展以及命令扩展默认情况下已启用,此处也需要。

setlocal EnableExtensions EnableDelayedExpansion
set "start=2"

FOR /L %%a in (1,1,3) DO (
    FOR /L %%b in (!start!,1,3) DO (
        echo !start! Before Call
        call :changestring
        FOR /L %%c in (1,1,3) DO (
            REM Nothing
        )
    )
)

endlocal
pause
goto :EOF

:changestring
echo %start% Before Set
set "start=1"
echo %start% After Set
pause
goto :EOF

环境变量start现在在第二个 FOR 命令行及其命令块中使用!start!引用两次,这意味着在第一行修改时启用了延迟扩展批处理文件。

Windows命令解释程序在运行此修改后的批处理文件时最多输出pause

setlocal EnableExtensions EnableDelayedExpansion

set "start=2"

FOR /L %a in (1 1 3) DO (FOR /L %b in (!start! 1 3) DO (
echo !start! Before Call
 call :changestring
 FOR /L %c in (1 1 3) DO (REM Nothing )
) )

(FOR /L %b in (!start! 1 3) DO (
echo !start! Before Call
 call :changestring
 FOR /L %c in (1 1 3) DO (REM Nothing )
) )

(
echo !start! Before Call
 call :changestring
 FOR /L %c in (1 1 3) DO (REM Nothing )
)
2 Before Call

echo 2 Before Set
2 Before Set

set "start=1"

echo 1 After Set
1 After Set

pause

预处理后,!start!都保留在命令块中未修改。所以现在它确实引用了环路变量start的当前值,它在执行循环期间被修改。

由于%start%命令行都不在命令块中,所以不需要在子例程中用!start!替换echo,因此每个命令行都是预处理的在紧接执行命令 ECHO 之前用环境变量的当前值替换%start%

我希望在预处理/解析单个命令行或整个命令块期间,立即延迟扩展环境变量之间的区别现在很明显。

顺便说一下:变量的名称start不好。 start可以用作环境变量名称,但也可以在同一个批处理文件中使用命令 START ,这样就很难区分环境变量start和命令start。更好的是作为变量名称,例如FirstNumber

使用FirstNumber代替start的优化代码,使用算术表达式减少数量,从而可以在不使用的情况下引用环境变量%!,但在算术表达式的评估期间使用环境变量的当前值。以2开头的第一个数字应该永远不会减少到低于1的值,这是二进制的原因,或者是1

@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FirstNumber=2"

FOR /L %%a in (1,1,3) DO (
    FOR /L %%b in (!FirstNumber!,1,3) DO (
        echo !FirstNumber! before decrement
        set /A "FirstNumber=FirstNumber - 1 | 1"
        FOR /L %%c in (1,1,3) DO REM Nothing
    )
)

endlocal

运行此批处理文件时,@echo off位于顶部的输出是:

2 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement
1 before decrement

当然set /A "FirstNumber=FirstNumber - 1 | 1"在这里只是环境变量赋值的标准字符串,即set "FirstNumber=1"。但我想证明环境变量只能通过算术表达式中的名称来引用,以便在评估表达式时使用环境变量的当前值。命令 SET

的帮助也描述了这种行为

要了解使用的命令及其工作原理,请打开命令提示符窗口,执行以下命令,并完全阅读为每个命令显示的所有帮助页面。

  • call /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • pause /?
  • rem /?
  • set /?
  • setlocal /?

结论:仔细阅读命令,应用程序或函数的帮助或文档,对于程序员或脚本编写者来说,总是很好的做法,以避免编写代码时出现问题。