修改for循环在批处理脚本中不使用delayedexpansion

在我努力理解for..do循环语法及其对%%variables的使用。 我经历了2个具体的例子/实现,其中一个for循环不使用DELAYEDEXPANSION ,另一个使用DELAYEDEXPANSION! 符号。 1st for循环似乎与Windows XP等较旧的操作系统兼容,而2nd for循环示例则不兼容。

具体地说,从这个答案中得到第1个for循环例子(与此有关),并且从这个答案中获取第2个for循环例子

这两个例子修改后的代码复制如下:

第一个循环

 for /f "tokens=2 delims==" %%a in ('wmic OS Get localdatetime /value') do set "dt=%%a" set "YY=%dt:~2,2%" set "YYYY=%dt:~0,4%" set "MM=%dt:~4,2%" set "DD=%dt:~6,2%" set "HH=%dt:~8,2%" set "Min=%dt:~10,2%" set "Sec=%dt:~12,2%" set "datestamp=%YYYY%%MM%%DD%" set "timestamp=%HH%%Min%%Sec%" echo datestamp: "%datestamp%" echo timestamp: "%timestamp%" 

第二个循环

 SETLOCAL ENABLEDELAYEDEXPANSION set "path_of_folder=C:\folderA\folderB" for /f "skip=5 tokens=1,2,4 delims= " %%a in ( 'dir /ad /tc "%path_of_folder%\."') do IF "%%c"=="." ( set "dt=%%a" set vara=%%a set varb=%%b echo !vara!, !varb! set day=!vara:~0,2! echo !day! ) 

由于我一直在阅读和看到延迟扩展(或!符号)与较旧的操作系统(例如Windows XP)不兼容的问题,所以我想看看如何编写像第一个循环一样的第二个循环; 即不使用DELAYEDEXPANSION

Solutions Collecting From Web of "修改for循环在批处理脚本中不使用delayedexpansion"

我详细地解释了aschipfl在他的评论中已经写得非常好。

这两个批处理文件也可以在Windows 2000和Windows XP上使用cmd.exe作为命令解释器。 批处理文件不能在MS-DOS,Windows 95和Windows 98上使用非常有限的command.com作为命令解释器。

一个命令可以用参数/?来执行 在命令提示符窗口中输出这个命令的帮助。 使用启用的命令扩展写入帮助时,表示仅支持基于Windows NT的Windows版本上的cmd.exe ,而不支持MS-DOS或Windows 9x使用command.com 。 这意味着例如for /F或者if /I或者call :Subroutine在Windows 9x上不可用,或者在基于Windows NT的Windows上显式禁用了命令扩展。 在Windows 9x上甚至不可能使用"%~1""%~nx1"

第一个批处理文件在FOR循环中只执行1次命令:

 set "dt=%%a" 

FOR循环完成后执行下面的所有其他命令。 换句话说,第一批处理文件中的FOR循环不使用命令块在FOR循环中运行多个命令。

每当Windows NT命令解释器检测到命令行上的命令块开始时,它就会在第一次在此命令行上执行命令之前处理整个命令块。

这意味着对于第二个批处理文件,所有使用%Variable%变量引用在命令FOR执行之前已经被扩展,然后命令块中的命令被执行,如上面FOR命令行所定义的变量的值。 这可以通过从批处理文件的第一行中删除@echo off或将其更改为@echo ON并从命令提示符窗口内运行批处理文件来看出,因为现在可以看到哪些命令行分别用( 。)定义了整个命令块。 .. )在通过命令解释器进行预处理后真正执行。

因此,无论何时在命令块中定义或修改环境变量,并且在相同的命令块中引用其值,都必须使用延迟扩展或使用替代方法。

一种解决方法如下所示:

 setlocal EnableExtensions DisableDelayedExpansion set "FolderPath=%SystemRoot%\System32" for /F "skip=5 tokens=1,2,4 delims= " %%a in ('dir /AD /TC "%FolderPath%\."') do if "%%c"=="." ( set "VarA=%%a" set "VarB=%%b" call echo %%VarA%%, %%VarB%% call set "Day=%%VarA:~0,2%% call echo %%Day%% ) endlocal pause 

由于在此批处理代码的顶部没有@echo off ,因此可以在执行批处理文件时看到这里发生了什么。 处理命令块时,每%%被修改为% 。 所以执行的是命令行。

 call echo %VarA%, %VarB% call set "Day=%VarA:~0,2% call echo %Day% 

CALL命令用于第二次处理其余行,以运行ECHOSET命令,其中环境变量引用被其对应的值替换,而没有或者没有字符串替换。

避免延迟扩展的另一个解决方法是使用子例程:

 @echo off setlocal EnableExtensions DisableDelayedExpansion set "FolderPath=%SystemRoot%\System32" for /F "skip=5 tokens=1,2,4 delims= " %%a in ('dir /AD /TC "%FolderPath%\."') do if "%%c"=="." call :ProcessCreationDate "%%a" "%%b" endlocal pause goto :EOF :ProcessCreationDate echo %~1, %~2 set "Day=%~1" set "Day=%Day:~0,2% echo %Day% goto :EOF 

一个子程序就像当前批处理文件中嵌入的另一个批处理文件。

第一个goto :EOF避免通过子程序的代码。

第二个goto :EOF如果上面的行是批处理文件的最后一行, goto :EOF将不是必需的。 但是,如果有更多的命令行在后面像第二个子程序一样添加,建议使用它。

第二个批处理文件用于获取指定文件夹的创建日期。 可以编写这个批处理文件而不使用延迟扩展和任何变通方法。

 @echo off setlocal EnableExtensions DisableDelayedExpansion set "FolderPath=%SystemRoot%\System32" for /F "skip=5 tokens=1,2,4 delims= " %%a in ('dir /ad /tc "%FolderPath%\." 2^>nul') do if "%%c"=="." set "CreationDate=%%a, %%b" & goto OutputDateAndDay echo Failed to get creation date of "%FolderPath%" endlocal pause goto :EOF :OutputDateAndDay echo %CreationDate% set "Day=%CreationDate:~0,2% echo %Day% endlocal pause 

一旦找到具有指定文件夹创建日期的兴趣线,将创建日期/时间分配给环境变量,并使用命令GOTO退出FOR循环以继续执行下面的标签。 有关&运算符的含义,请参阅使用Windows批处理文件的多行命令的单行 。

这个解决方案比所有其他方法都要好,因为FOR循环只用一个命令IFSETGOTO执行单个命令行一次,这使得这个解决方案最快。 并且,当不能确定目录的创建日期时,它会输出一个错误消息,因为该目录根本不存在。

当然,在确定并输出目录的创建日期之后,也可以在其他解决方案上添加GOTO命令以退出FOR循环。 尽管如此,最后的解决方案仍然是最快的,而且在我看来,这个任务是最好的解决方案。

顺便说一句:所有发布的批处理文件的例子在Windows XP上测试,并产生预期的输出。