23214

Modify for loop to not use delayedexpansion in batch script

Question:

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.

<em>Specifically, the <strong>1st for loop</strong> example is taken from this <a href="https://stackoverflow.com/a/44948574/6003691" rel="nofollow">answer</a> (which is related to <a href="https://stackoverflow.com/a/44929240/6003691" rel="nofollow">this</a>) and the <strong>2nd for loop</strong> example is taken from this <a href="https://stackoverflow.com/a/19131662/6003691" rel="nofollow">answer</a></em>.

Modified code for both examples copied below:

<strong>1st for loop</strong>

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

<strong>2nd for loop</strong>

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 <strong>how to write the 2nd loop like the 1st loop; i.e. without the use of DELAYEDEXPANSION</strong>.

Answer1:

I explain in detail what <a href="https://stackoverflow.com/users/5047996/aschipfl" rel="nofollow">aschipfl</a> 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 <strong>with enabled command extensions</strong> 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 <strong>FOR</strong> loop only 1 command exactly 1 times:

set "dt=%%a"

All other commands below are executed after <strong>FOR</strong> loop finished. In other words the <strong>FOR</strong> loop in first batch file does not use a command block to run multiple commands within the <strong>FOR</strong> 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 <strong>FOR</strong> is executed and the commands in the command block are then executed with the values of the variables as defined above <strong>FOR</strong> 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 <strong>CALL</strong> is used to process the rest of the line a second time to run the <strong>ECHO</strong> and the <strong>SET</strong> 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 <strong>FOR</strong> loop is exited with using command <strong>GOTO</strong> to continue execution on a label below. For the meaning of & operator see <a href="https://stackoverflow.com/a/25344009/3074564" rel="nofollow">Single line with multiple commands using Windows batch file</a>.

This solution is better than all other methods because the <strong>FOR</strong> loop executes the single command line with the 3 commands <strong>IF</strong>, <strong>SET</strong> and <strong>GOTO</strong> 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 <strong>GOTO</strong> command also on the other solutions to exit <strong>FOR</strong> 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.

Recommend

  • Geo Fix not working in Android SDK 2.2
  • Windows batch string manipulation in loop
  • True privateness in Python
  • mysql table locked after php crashes
  • Is it possible to make imports depend on the location of my Lua script instead of the current direct
  • Reflection / C# typing errors when publishing an F# class implementing an interface
  • View Paypal shopping cart contents on my site
  • Timeout a query
  • Creating a C++ function that calls other Lua function
  • ASP.NET windows authentication should always ask for credentials
  • Thread synchronization with syncwarp
  • in batch how do i use taskkill properly
  • Changing Jupyter Notebook start up folder by modifying “start in” not working any more
  • VSCode change debug shell to bash on windows
  • Error processing multiple files
  • How can I run DataNucleus Bytecode Enhancer from SBT?
  • Jquery Knockout: ko.computed() vs classic function?
  • aapt.exe'' finished with non-zero exit value 1
  • How to specify input and output paths from cmd.exe for a PowerShell script?
  • How to synchronize jQuery dialog box to act like alert() of Javascript
  • Tamper-proof configuration files in .NET?
  • Code in Job's Script Block after Start-Process Does not Execute
  • Unable to decode certificate at client new X509Certificate2()
  • Needing to do .toArray() to get output of mongodb .find() on key name not value
  • Jenkins: How To Build multiple projects from a TFS repository?
  • How do I fake an specific browser client when using Java's Net library?
  • Running a C# exe file
  • Symfony2: How to get request parameter
  • ActionScript 2 vs ActionScript 3 performance
  • ORA-29908: missing primary invocation for ancillary operator
  • Do create extension work in single-user mode in postgres?
  • Apache 2.4 - remove | delete | uninstall
  • R: gsub and capture
  • jqPlot EnhancedLegendRenderer plugin does not toggle series for Pie charts
  • Run Powershell script from inside other Powershell script with dynamic redirection to file
  • How do I rollback to a specific git commit
  • Is there a mandatory requirement to switch app.yaml?
  • Comma separated Values
  • Busy indicator not showing up in wpf window [duplicate]
  • How to load view controller without button in storyboard?