0

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

具体来说,第一个 for 循环示例取自此答案(与相关),第二个 for 循环示例取自此答案

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

第一个 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%"

第二个for循环

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.

4

1 回答 1

1

我详细解释了aschipfl在他的评论中写的绝对正确的内容。

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

可以/?在命令提示符窗口中使用参数执行命令以获取该命令的帮助输出。当在帮助中使用启用的命令扩展编写时,这意味着仅cmd.exe在基于 Windows NT 的 Windows 版本上受支持,而在 MS-DOS 或 Windows 9x 上使用command.com. 这意味着例如在 Windows 9xfor /F或基于 Windows NT 且命令扩展被明确禁用的 Windows 上不可用if /Icall :Subroutine在 Windows 9x 上,甚至无法使用"%~1""%~nx1".

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

set "dt=%%a"

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

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

这意味着对于第二个批处理文件,所有使用的变量引用在执行命令FOR%Variable%之前已经展开,然后使用上面定义的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如果上面的行是批处理文件的最后一行,则不需要第二个。但是还是建议使用它,以防稍后在下面添加更多命令行,例如第二个子例程。

第二个批处理文件用于获取创建指定文件夹的日期。可以在不使用延迟扩展和任何解决方法的情况下对该批处理文件进行编码。

@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循环只执行 3 个命令IFSETGOTO的单个命令行,这使得此解决方案最快。当由于目录根本不存在而无法确定目录的创建日期时,它会输出错误消息。

当然,一旦确定并输出了目录的创建日期,也可以在其他解决方案上添加GOTO命令以退出FOR循环。然而,最后一个解决方案是最快的,在我看来是完成这项任务的最佳解决方案。

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

于 2017-07-07T06:08:50.280 回答