Modify for loop to not use delayedexpansion in batch script

偶尔善良 提交于 2020-01-17 10:54:10

问题


In my efforts to understand the for..do loops syntax and their use of %% variables. I have gone through 2 specific examples/implementations where the one for loop does not use DELAYEDEXPANSION and another where it does use DELAYEDEXPANSION with the ! notation. The 1st for loop appears to be compatible with older OSs like the Windows XP whereas the 2nd for loop example does not.

Specifically, the 1st for loop example is taken from this answer (which is related to this) and the 2nd for loop example is taken from this answer.

Modified code for both examples copied below:

1st for loop

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%"

2nd for loop

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!
)

Since I have been reading and seeing issues where delayed expansion (or the ! notation) is not compatible with older OSs (e.g. Windows XP), I would like to see how to write the 2nd loop like the 1st loop; i.e. without the use of DELAYEDEXPANSION.


回答1:


I explain in detail what aschipfl wrote already absolutely right in his comment.

Both batch files work also on Windows 2000 and Windows XP using also cmd.exe as command interpreter. The batch files do not work on MS-DOS, Windows 95 and Windows 98 using very limited command.com as command interpreter.

A command can be executed with parameter /? in a command prompt window to get output the help for this command. When in help is written with enabled command extensions it means supported only by cmd.exe on Windows NT based Windows versions and not supported by MS-DOS or Windows 9x using command.com. That means for example for /F or if /I or call :Subroutine are not available on Windows 9x, or on Windows NT based Windows with command extensions explicitly disabled. On Windows 9x it is not even possible to use "%~1" or "%~nx1".

The first batch file executes in FOR loop only 1 command exactly 1 times:

set "dt=%%a"

All other commands below are executed after FOR loop finished. In other words the FOR loop in first batch file does not use a command block to run multiple commands within the FOR loop.

Whenever Windows NT command interpreter detects the beginning of a command block on a command line, it processes the entire command block before executing the command on this command line the first time.

This means for second batch file all variable references using %Variable% are expanded already before the command FOR is executed and the commands in the command block are then executed with the values of the variables as defined above FOR command line. This can be seen by removing @echo off from first line of batch file or change it to @echo ON and run the batch file from within a command prompt window because now it can be seen which command lines respectively entire command blocks defined with ( ... ) are really executed after preprocessing by command interpreter.

So whenever an environment variable is defined or modified within a command block and its value is referenced in same command block it is necessary to use delayed expansion or use workarounds.

One workaround is demonstrated below:

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

As there is no @echo off at top of this batch code it can be seen on executing the batch file what happens here. Each %% is modified on processing the command block to just %. So executed are the command lines.

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

The command CALL is used to process the rest of the line a second time to run the ECHO and the SET commands with environment variable references replaced by their corresponding values without or with string substitution.

Another workaround to avoid delayed expansion is using a subroutine:

@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

A subroutine is like another batch file embedded in current batch file.

The first goto :EOF avoids a fall through to the code of the subroutine.

The second goto :EOF would not be necessary if the line above is the last line of the batch file. But it is recommended to use it nevertheless in case of more command lines are ever added later below like a second subroutine.

The second batch file is for getting the day on which the specified folder was created. It would be possible to code this batch file without usage of delayed expansion and any workarounds.

@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

Once the line of interest with the creation date of specified folder is found, the creation date/time is assigned to an environment variable and the FOR loop is exited with using command GOTO to continue execution on a label below. For the meaning of & operator see Single line with multiple commands using Windows batch file.

This solution is better than all other methods because the FOR loop executes the single command line with the 3 commands IF, SET and GOTO only one times which makes this solution the fastest. And it outputs an error message when it was not possible to determine the creation date of the directory because the directory does not exist at all.

Of course it would be possible to add a GOTO command also on the other solutions to exit FOR loop once the creation date of the directory was determined and output. The last solution is nevertheless the fastest and in my point of view best one for this task.

BTW: All posted batch file examples were tested on Windows XP and produced the expected output.



来源:https://stackoverflow.com/questions/44953403/modify-for-loop-to-not-use-delayedexpansion-in-batch-script

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!