修改for循环以在批处理脚本

时间:2017-07-06 15:38:16

标签: windows batch-file windows-xp delayedvariableexpansion

我努力了解for..do循环语法及其对%%变量的使用。我已经完成了两个具体的示例/实现,其中一个for循环不使用DELAYEDEXPANSION,另一个使用DELAYEDEXPANSION符号!。第1个for循环似乎与Windows XP等旧操作系统兼容,而第2个for循环示例则不兼容。

具体来说, 1st for loop 示例来自此answer(与this相关)和第二个for循环示例来自此answer

以下复制的两个示例的修改代码:

第一个循环

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

1 个答案:

答案 0 :(得分:2)

我详细解释了aschipfl在评论中已经完全正确写的内容。

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

可以在命令提示符窗口中使用参数/?执行命令,以获取此命令的帮助。当在帮助中写入并启用了命令扩展时,它表示仅在基于Windows NT的Windows版本上由cmd.exe支持,而在MS-DOS或Windows 9x上不支持command.com。这意味着,例如{9}或for /Fif /I在Windows 9x或基于Windows NT的Windows上不可用,并且明确禁用了命令扩展名。在Windows 9x上,甚至无法使用call :Subroutine"%~1"

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

"%~nx1"

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

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

这意味着对于第二个批处理文件,在执行命令 FOR 之前已经扩展了使用set "dt=%%a" 的所有变量引用,然后使用变量的值执行命令块中的命令如上所述 FOR 命令行。通过从批处理文件的第一行删除%Variable%或将其更改为@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 SET 命令引用被替换为没有或没有字符串替换的相应值。

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

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

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

第一个@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 <退出 FOR 循环/ strong>继续执行下面的标签。有关@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 运算符的含义,请参阅Single line with multiple commands using Windows batch file

此解决方案优于所有其他方法,因为 FOR 循环使用3个命令执行单个命令行 IF SET 和< strong> GOTO 只有一次使这个解决方案最快。当无法确定目录的创建日期时,它会输出错误消息,因为该目录根本不存在。

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

BTW:所有发布的批处理文件示例都在Windows XP上进行了测试,并产生了预期的输出。