windowsfor-loopbatch-filestring-substitutiondelayedvariableexpansion

Struggling to perform string substitution within a nested for loop


In a Windows cmd shell batch script, I am trying to craft a mechanism to iterate over a series of directories which all have their name in the form of x.Year-Month-Day, where x is a sequential value, from 0 up to a defined limit. I want to move/rename the directories, such that the last one is deleted, and the rest increment their index by one each time the script is run. The script doesn't know what the date values will be in the individual directory names.

To wit:

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION

REM Set work variables:
SET source=C:\Program Files\AppName\WorkDir
SET localDest=%source%\Data Backups
SET maxBackups=5

REM Get to work... 

REM Deduct 1 from maxBackups b/c of 0-based count:
SET /A limit=maxBackups-1

REM Remove any folders indexed at the limit...
FOR /D %%f IN ("%localDest%\%limit%.*") DO rd /S /Q "%%f"    

REM Iterate through the backup folders...
FOR /L %%i IN (%limit%,-1,1) DO (
    SET /A c=%%i-1
    FOR /D %%d IN ("%localDest%\!c!.*") DO (
        SET src=%localDest%\!c!.
        SET dst=%localDest%\%%i.
        REM *** Can't get this assignment to work! ***
        SET file=%%%d:!src!=!dst!%%
        MOVE /Y "%%d" "!file!"
    )
)

REM Now copy the current data folder to a new '0' index backup folder...
MD "%localDest%\0.%date%"
ROBOCOPY "%source%\Data" "%localDest%\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V %1 %2 %3 %4 %5

:End

As the comment indicates, I've tried innumerable permutations of the string-substitution, trying to create a new name for the directory with the modified index, but I cannot get the processor to do the substitution!

I've tried:

SET file=%%d:!src!=!dst!

Which produces just the three strings with the applicable punctuation, (and adding a trailing % doesn't do anything).

Prepending another % ...

SET file=%%%d:!src!=!dst!%

...produces a string that starts with a %, but only contains the values from !src! and !dst! separated by an equals sign, (=).

And, once again, adding another % to the end of the string, merely adds one to the end of the output.

I've tried innumerable combinations of % on the front and end of the assignment, but never seem to be able to get the substitution to work. I can't help but wonder if the colon from the path, (C:\), is messing with the parser. But surely a cmd shell batch processor, would be written with the understanding that paths including colons, would be exceedingly common things for such scripts to encounter, no?

I've been reading and re-reading articles here and around the web talking about how to do these things, but all the examples seem to be one-dimensional, (i.e. 'How to use delayed parsing', 'How to use variables inside loops', 'How to do string substitution' etc.). I haven't found anything demonstrating/trying to parse a string, (which contains a colon!), in real time, with value-substitution, from within a nested loop is discussed/explained/demonstrated. Trying to make it work has, thus far, proved entirely futile, and I'm at the point now of not knowing what to do/try next. What am I missing/not getting correct?


Solution

  • Thanks to all of the suggestions, comments and examples, I finally managed to cobble together a solution to my issue!

    It really came down to not realising/understanding that FOR substitution parameters didn't function like 'normal' environment variables wrt the string manipulation features. Also, learning about pushd/popd was pretty awesome too!

    Here's a pared-down version of what I came up with:

    @ECHO OFF
    SETLOCAL ENABLEDELAYEDEXPANSION
    
    REM Set work variables:
    SET source=C:\Program Files\AppName\WorkDir
    SET localDest=%source%\Data Backups
    REM NOTE: 'maxBackups' CANNOT be greater than 10!
    SET maxBackups=5
    
    REM Get to work... 
    
    REM Deduct 1 from maxBackups b/c of 0-based count:
    SET /A limit=maxBackups-1
    PUSHD %localDest%
    
    REM Remove any/all folders indexed at the limit...
    FOR /D %%f IN (".\%limit%.*") DO rd /S /Q "%%f"    
    
    REM Iterate through the existing backup folders...
    FOR /L %%i IN (%limit%,-1,1) DO (
        SET /A c=%%i-1
        FOR /D %%d IN (".\!c!.*") DO (
            SET folderSuffix=%%d
            MOVE /Y "%%d" ".\%%i!folderSuffix:~3!"
        )
    )
    
    REM Now create a new '0' index backup folder...
    MD ".\0.%date%"
    
    REM ...and copy the current data folder's contents to it!
    ROBOCOPY "%source%\Data" ".\0.%date%" *.* /S /E /COPYALL /ZB /R:5 /W:60 /XJ /NP /V /J /TEE /COMPRESS %1 %2 %3 %4 %5
    
    :End
    POPD
    

    ..and yes, I'm aware that things will go tragically sideways if maxBackups exceeds 10, but each folder is nearly 20GB in size currently (and growing) and I don't genuinely forsee us ever keeping many more than the handful (i.e. ~5) that we're currently retaining. I just felt that using the variable made implementing tweaks/changes easier down the road if we ever decided we wanted to add/remove a few (i.e. going with 7 to have daily images, or down to 2 or 3 to save space, or something similar 🤷‍♀️).