Delayed expansion and arrays in Win 10 batch files


  1. Posts : 4,187
    Windows 11 Pro, 22H2
       #1

    Delayed expansion and arrays in Win 10 batch files


    I've been researching this one for hours, but can't make any headway...

    I have a folder with 3 files in it that looks like this:

    ISO Images
    > Image 1.iso
    > Image 2.iso
    > Image 3.iso

    I have a batch file that is doing some processing of these ISO images but I'm having a really hard time figuring out some behaviors.

    Below is a sample of code showing where my quandary is. I expect the FOR loop to place the full path and filename of each file into an array called SourceFile[x], with x being a number from 1 to 3.

    I have a few echo statements in the FOR loop that show me that all values are exactly what I expect and that everything within the FOR loop is working fine.

    However, after I drop out of the FOR loop, the variables SourceFile[x] do not seem to hold their values any longer.




    Here a sample clip of some code and what actually happens:

    Note: I run this batch file as admin

    The code clip:
    ________________
    @echo off
    setlocal enabledelayedexpansion

    set SourceDir="D:\ISO Images"
    set /a count=0

    for %%a in (!SourceDir!\*.iso) do (
    set /a count=count+1
    set "SourceFile[%count%]=%%a"

    echo The value of count is: !count!
    echo The value of a is: %%a
    echo The value of SourceFile[!count!] is: !SourceFile[%count%]!
    echo.
    )
    echo.
    echo.
    echo The current value of count is: %count%
    echo %SourceFile[%count%]%
    echo %SourceFile[1]%
    ________________


    Here is the resulting output:

    _____________________
    The value of count is: 1
    The value of a is: D:\ISO Images\Image 1.ISO
    The value of SourceFile[1] is: D:\ISO Images\Image 1.ISO

    The value of count is: 2
    The value of a is: D:\ISO Images\Image 2.ISO
    The value of SourceFile[2] is: D:\ISO Images\Image 2.ISO

    The value of count is: 3
    The value of a is: D:\ISO Images\Image 3.ISO
    The value of SourceFile[3] is: D:\ISO Images\Image 3.ISO



    The current value of count is: 3
    count
    ECHO is off.
    Press any key to continue . . .
    __________________

    So there are a couple of questions:

    1) Why is my array (SourceFile[x]) not holding values after I come out of the FOR loop?

    2)This question requires a little explanation...

    First, I need to explain my understanding of delayed expansion to make sure I have this correct. My understanding is that within a block (within the parenthesis of a "for" or "if" structure, for example, or on a single line of code), variables are expanded, or assigned their values, before the code is actually executed. My understanding is that with delayed expansion enabled, the variables are not expanded (assigned values) until the last possible moment, in other words, when the code executes and actually references the variable. To do this, the variable has to be referenced like this: !MyVariable!, and not like this: %MyVariable%.

    From my testing it seems that setting the value of a variable with a "set" does not need to use the ! rather than the %. For example, within the FOR loop, I need to use
    set "SourceFile[%count%]=%%a"
    NOT
    set "SourceFile[!count!]=%%a"

    So with all that said, in the line that reads
    echo The value of SourceFile[!count!] is: !SourceFile[%count%]!
    why do I need to reference the variable at the end of that line as !SourceFile[%count%]! and not as !SourceFile[!count!]!

    If I reference it as !SourceFile[!count!]! then my output reads
    The value of SourceFile[3] is: count
    rather than
    The value of SourceFile[3] is: D:\ISO Images\Image 3.ISO

    So to summarize, there is question regarding the syntax for delayed expansion and the question about why my array variables are losing their values after coming out of the FOR loop.
      My Computers


  2. Posts : 16,950
    Windows 10 Home x64 Version 22H2 Build 19045.4170
       #2

    To help with diagnosis, add a more general echo that will list all your SourceFile* variables
    Code:
    Set SourceFile
    Denis
    Last edited by Try3; 15 Dec 2018 at 15:51.
      My Computer


  3. Posts : 4,187
    Windows 11 Pro, 22H2
    Thread Starter
       #3

    Denis,

    Thanks for the response. I tried your suggestion but it's only returning this:

    set SourceFile
    Press any key to continue . . .

    I also tried "echo %SourceFile%" (without the set) outside of the FOR loop and "echo !SourceFile!" but that simply returns "ECHO is off."
      My Computers


  4. Posts : 16,950
    Windows 10 Home x64 Version 22H2 Build 19045.4170
       #4

    Sorry, I failed to check what I wrote properly. Now corrected.

    I meant to suggest adding
    Code:
    Set SourceFile
    because that will list all the variables beginning with the string SourceFile. Here is a demo bat
    Code:
    set fred1=aaaa
    set fred2=bbbb
    set fred3=cccc
    set fred
    pause
    that returns, in response to the set fred line,
    Code:
    fred1=aaaa
    fred2=bbbb
    fred3=cccc
    Delayed expansion and arrays in Win 10 batch files-setdemo.png

    Denis
      My Computer


  5. Posts : 4,187
    Windows 11 Pro, 22H2
    Thread Starter
       #5

    Okay, that was a seriously clever idea. That revealed that my variables do actually contain the correct valuse and that it's just how I am trying to print the value of the variables that seems to be at fault. I have some new ideas that I'm going to try.

    Sorry for the lack of details here but I'm a little short on time at the moment. I'll respond back with more detail later.

    In the meantime, thanks for the brilliant idea. I didn't realize that was possible!
      My Computers


  6. Posts : 16,950
    Windows 10 Home x64 Version 22H2 Build 19045.4170
       #6

    It would have been more of a brilliant idea if I had written it correctly the first time around though.

    I think that your problem is how you are referring to the variables after the For loop. The problem is not caused by DelayedExpansion itself. Variables made from other variables need to be referenced in a particular way. Added later - @bro67 links probably do a better job than my basic examples below.

    Here is a simple demo -
    Code:
    prompt $g
    Set X=New
    Set Y=This variable ends up not being used at all
    Set YNew=This is the variable that actually gets echoed
    call echo %%Y%X%%%
    Pause at EndVarNamingDemo
    and it produces this output
    Code:
    >Set X=New
    >Set Y=This variable ends up not being used at all
    >Set YNew=This is the variable that actually gets echoed
    >call echo %YNew%
    This is the variable that actually gets echoed
    >Pause at EndVarNamingDemo
    Press any key to continue . . .
    Delayed expansion and arrays in Win 10 batch files-simplerdemo.png

    The combination of the Call before the Echo forces windows to parse the weird line call echo %%Y%X%%% twice
    The first time it parses, it can only understand and resolve %X% but as it parses it also reduces the remaining %% to %
    So, during the second parse, all it sees is call echo %YNew% and it is quite happy to implement that command
    It leaves no visible trace of its double parsing which can make things difficult to debug

    By the way, I avoid DelayedExpansion whenever I can because I find the construction of multi-commands difficult to debug [I had to read yours several times before I felt I could usefully respond]. Instead, if a For loop involves more than a single command, I use subroutines and I find those easier to debug. This is a harmless example of the structure I use

    Code:
    ::Constants
    Set TargetFolder=%~dp0
    Set TargetFolder=%TargetFolder:~0,-1%
    Set TargetFiles=*.*
    ::Inital conditions
    set /a count=0
    :: Process each of the TargetFiles in the TargetFolder
    FOR /F "tokens=*" %%X IN (' Dir "%TargetFolder%\%TargetFiles%" /b ') Do call :TestSub "%%X"
    GoTo EndSubRoutineDemo
    :TestSub
    :: Process the passed parameter
    GoTo :EOF
    :EndSubRoutineDemo
    Pause at EndSubRoutineDemo just to have a look during this test
    Denis
    Last edited by Try3; 15 Dec 2018 at 20:04.
      My Computer


  7. Posts : 9,790
    Mac OS Catalina
       #7

    See Issue storing a value in a batch file - Stack Overflow It is about storing a value for a array. Goes back to how Basic worked. Steve Jansen's website helps a bit more. Windows Batch Scripting: Variables - /* steve jansen */ Just remember that a storage device may not always be "D", since there could be a storage device already assigned that value. More help on arrays. Batch Script Arrays
      My Computer


  8. Posts : 4,187
    Windows 11 Pro, 22H2
    Thread Starter
       #8

    That did the trick! Using "call" solved the roadblock I was facing.

    What I'm doing is revising a rough draft of a batch file I had created a few weeks ago and am now polishing up. The batch file originally would grab a Windows ISO image, open it, copy the contents to a temporary location, inject all the latest Windows updates, and then create a new ISO image with all the updates slipstreamed in.

    Because I have several ISO images with various unattended setup configurations, I just want to update the batch file now to go through all the ISO images in a folder and update all of them.

    Thanks to your help, my roadbloack is now cleared.

    Thanks for taking the time to respond. I can't begin to tell you how frustrating it was to spend hours getting nowhere.
      My Computers


 

  Related Discussions
Our Sites
Site Links
About Us
Windows 10 Forums is an independent web site and has not been authorized, sponsored, or otherwise approved by Microsoft Corporation. "Windows 10" and related materials are trademarks of Microsoft Corp.

© Designer Media Ltd
All times are GMT -5. The time now is 08:17.
Find Us




Windows 10 Forums