如何为/F 设置循环中的变量

我做了这个代码

dir /B /S %RepToRead% > %FileName%


for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)

echo %%a工作正常,但 echo %z%返回“回声禁用”。

我需要设置% z% ,因为我想像 %z:~7%那样分割变量

有什么想法吗?

210762 次浏览

您可能需要 SETLOCAL ENABLEDELAYEDEXPANSION。请参阅 https://devblogs.microsoft.com/oldnewthing/20060823-00/?p=29993了解详细信息。

基本上: 正常的 %variables%cmd.exe读取命令后立即展开。在你的例子中,“命令”是整个

for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)

循环。此时 z还没有值,所以 echo %z%变成了 echo。然后执行循环并设置 z,但不再使用它的值。

SETLOCAL ENABLEDELAYEDEXPANSION启用另一种语法 !variable!。这也会扩展变量,但是只在执行每个(子)命令之前这样做。

SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)

这将在每次运行 echo时给出 z的当前值。

在 for 循环和括号范围内设置和使用变量有两种方法。

  1. setlocal enabledelayedexpansion请参阅 setlocal /?以获得帮助。这只适用于 XP/2000或更新版本的 Windows。 然后在循环中使用 !variable!而不是 %variable%...

  2. 使用批处理 goto 标签 :Label创建一个批处理函数。

    例如:

    for /F "tokens=*" %%a in ('type %FileName%') do call :Foo %%a
    goto End
    
    
    :Foo
    set z=%1
    echo %z%
    echo %1
    goto :eof
    
    
    :End
    

    批处理函数是非常有用的机制。

我在这上面挣扎了好几个小时。 这是注册命令行 vars 的循环。 示例: Register.bat/param1: value1/param2: value2

要做的就是循环所有命令行参数, 并将具有正确名称的变量设置为值。

在那之后,你只需要 Set value = ! Set value2 = !

不管给定的参数是什么顺序(所谓的命名参数)。 注意! < > ! ,而不是% < >% 。

SETLOCAL ENABLEDELAYEDEXPANSION


FOR %%P IN (%*) DO (
call :processParam %%P
)


goto:End


:processParam [%1 - param]


@echo "processparam : %1"
FOR /F "tokens=1,2 delims=:" %%G IN ("%1") DO (
@echo a,b %%G %%H
set nameWithSlash=%%G
set name=!nameWithSlash:~1!
@echo n=!name!
set value=%%H
set !name!=!value!
)
goto :eof


:End

以下措施应该奏效:

setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)

试试这个:

setlocal EnableDelayedExpansion


...


for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)

我知道这不是问什么,但我受益于这个方法,当试图设置一个“循环”内的变量。使用数组。替代实现选项。

SETLOCAL ENABLEDELAYEDEXPANSION


...


set Services[0]=SERVICE1
set Services[1]=SERVICE2
set Services[2]=SERVICE3


set "i=0"


:ServicesLoop
if defined Services[%i%] (
set SERVICE=!Services[%i%]!


echo CurrentService: !SERVICE!


set /a "i+=1"
GOTO :ServicesLoop
)
set list = a1-2019 a3-2018 a4-2017
setlocal enabledelayedexpansion
set backup=
set bb1=


for /d %%d in (%list%) do (
set td=%%d
set x=!td!
set y=!td!
set y=!y:~-4!
if !y! gtr !bb1! (
set bb1=!y!
set backup=!x!
)
)


rem: backup will be 2019
echo %backup%

为了扩展这个问题的答案,我来到这里想要更好地理解它,所以我写了这篇文章来解释它,也帮助了我。

它有 setlocal DisableDelayedExpansion在那里,所以你可以本地设置这之间的 setlocal EnableDelayedExpansion和它的愿望。

@echo off
title %~nx0
for /f "tokens=*" %%A in ("Some Thing") do (
setlocal EnableDelayedExpansion
set z=%%A
echo !z!        Echoing the assigned variable in setlocal scope.
echo %%A        Echoing the variable in local scope.
setlocal DisableDelayedExpansion
echo !z!        &rem !z!           Neither of these now work, which makes sense.
echo %z%        &rem ECHO is off.  Neither of these now work, which makes sense.
echo %%A        Echoing the variable in its local scope, will always work.
)

使用% var% 、 ! var! 和%%% 的批处理代码的简单示例。

在这个示例代码中,我们希望使用内置的 TIME (使用 TIME,因为它总是自动变化)来捕获开始时间:

密码:

@echo off
setlocal enabledelayedexpansion
SET "SERVICES_LIST=MMS ARSM MMS2"
SET START=%TIME%
SET "LAST_SERVICE="


for %%A in (%SERVICES_LIST%) do (
SET START=!TIME!
CALL :SOME_FUNCTION %%A
SET "LAST_SERVICE=%%A"
ping -n 5 127.0.0.1 > NUL
SET OTHER=!START!
if !OTHER! EQU !START! (
echo !OTHER! is equal to !START! as expected
) ELSE (
echo NOTHING
)
)
ECHO Last service run was %LAST_SERVICE%


:: Function declared like this
:SOME_FUNCTION
echo Running: %1
EXIT /B 0

关于代码的注释:

  • 使用启用延迟扩展
  • 前三条 SET 线是典型的 使用 SET 命令,大部分时间使用这个命令。
  • 下一行是 for 循环,必须使用%% A 进行迭代,如果循环内部有循环,则使用%% B 不能使用长变量名。
  • 要访问更改过的变量(如时间变量) ,必须使用! !还是一起来! !(已启用延迟扩展)。
  • 循环 for 循环时,每次迭代都作为% A 变量访问。
  • For 循环中的代码指出了设置变量的各种方法。看着‘ SET OTHER = !开始!’,如果您要更改为 SETOTER =% START% ,您将看到原因! !是必须的。(提示: 您将看不到任何东西)输出。
  • 简而言之! ! 更可能在循环内部需要,通常是% var% ,%% 总是 for 循环。

进一步阅读

使用以下链接可以确定更详细的原因:

如果访问作用域外的变量,则可以使用宏

@echo off
::Define macro
set "sset=set"


for /l %%a in (1,1,4) do (
::set in loop
%sset% /a "x[%%a]=%%a*%%a"
    

if %%a equ 4 (
:: set in condition
%sset% "x[%%a]=x Condition"
%sset% "y=y Condition"
)
)


echo x1=%x[1]%  x2=%x[2]%  x3=%x[3]%  x4=%x[4]%  y=%y%


:: Bonus. enableDelayedExpansion used to access massive from the loop
setlocal enableDelayedExpansion


echo Echo from the loop
for /l %%a in (1,1,4) do (
::echo in one line - echo|set /p =
echo|set /p "=x%%a=!x[%%a]!  "
if %%a equ 4 echo y=%y%
)
pause