如何在 /F 的循环内设置变量

Posted

技术标签:

【中文标题】如何在 /F 的循环内设置变量【英文标题】:How to set a variable inside a loop for /F 【发布时间】:2021-06-02 22:31:06 【问题描述】:

我编写了这段代码

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% 这样拆分变量

有什么想法吗?

【问题讨论】:

我认识到这并不能直接回答问题,但也许解决方案是切换到像 powershell 或 bash 这样的健全的脚本语言 Batch script for loop won't set variable的可能重复 【参考方案1】:

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

    setlocal enabledelayedexpansion 请参阅setlocal /? 寻求帮助。这仅适用于 XP/2000 或更新版本的 Windows。 然后在循环内使用!variable! 而不是%variable%...

    使用批处理 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
    

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

【讨论】:

double %% 是在 .bat 文件中运行它而不是直接在命令行中运行的问题。 这就是我讨厌批处理的原因 当你需要做一些字符串替换时(比如here),这个标签方法很有效。我无法使用 ENABLEDELAYEDEXPANSION 进行替换。 是的,这就是我们都讨厌语法的原因【参考方案2】:

你可能想要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 的当前值。

【讨论】:

你是对的。我通过使用 echo !z! 找到了解决方案!并在顶部添加 setlocal enablelayedexpansion【参考方案3】:

我为此奋斗了好几个小时。 这是我注册命令行变量的循环。 示例:Register.bat /param1:value1 /param2:value2

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

之后,您可以使用 设置值=!param1! 设置 value2=!param2!

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

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    

【讨论】:

谢谢。我试图在 for 循环中进行子字符串检测,而其他示例没有这个有用。【参考方案4】:

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

在这个示例代码中,这里的重点是我们要使用内置变量 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

代码注释:

使用 enabledelayedexpansion 前三个 SET 行是典型的 SET 命令的使用,大部分时间都使用它。 下一行是 for 循环,必须使用 %%A 进行迭代,如果其中有循环,则必须使用 %%B 等等。你不能使用长变量名。 要访问已更改的变量,例如时间变量,您必须使用 !!或设置 !! (已启用延迟扩展)。 在 for 循环中循环时,每次迭代都作为 %%A 变量访问。 for 循环中的代码指出了设置变量的各种方法。查看“SET OTHER=!START!”,如果您要更改为 SET OTHER=%START%,您会明白为什么!是需要的。 (提示:您将看到 NOTHING)输出。 简而言之!更可能需要在循环内部,通常为 %var%,%% 始终是 for 循环。

进一步阅读

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

Difference between %variable% and !variable! in batch file Variable usage in batch file

【讨论】:

【参考方案5】:
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% 

【讨论】:

【参考方案6】:

为了扩展答案,我来这里是为了更好地理解,所以我写了这个可以解释它并帮助我。

其中有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.
  )

【讨论】:

【参考方案7】:

如果您访问范围之外的变量,则可以使用宏

@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

【讨论】:

【参考方案8】:

试试这个:

setlocal EnableDelayedExpansion

...

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

【讨论】:

【参考方案9】:

我知道这不是所要求的,但是当我尝试在“循环”中设置变量时,我从这种方法中受益。使用数组。替代实施选项。

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
)

【讨论】:

【参考方案10】:

以下应该有效:

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

【讨论】:

这行不通。这正是 OP 发布的无效

以上是关于如何在 /F 的循环内设置变量的主要内容,如果未能解决你的问题,请参考以下文章

我将如何隔离或仅将第二个元素存储在批处理文件内的 for 循环中?

电脑JAVA环境变量如何设置

js 如何保存循环内的变量,然后循环外使用这个变量?

如果全局不起作用,在Python中访问for循环之外的变量?

如何使用定时全局变量影响 bash while 循环?

如何强制 Python 在循环内创建新变量/新范围? [复制]