Nested batch for loops

82,639

Solution 1

What if you used pushd !CTD! and popd, and let FOR /R default to using the current directory?

Solution 2

Just to give an example of a nested loop that works:

@echo off
SetLocal

set B=alpha beta gamma
set A=eins zwo

FOR %%b in (%B%) do (
  FOR %%a in (%A% %%b) DO (
    echo %%b -^> %%a
  )
)

The output (at least on Windows 7) is

alpha -> eins
alpha -> zwo
alpha -> alpha
beta -> eins
beta -> zwo
beta -> beta
gamma -> eins
gamma -> zwo
gamma -> gamma

This supports jeb's observation that variable expansion in loops works if they occur inside parenthesis (even without delayed expansion).

Solution 3

Because nobody has mentioned it, here is the solution using batch subroutines and the CALL command.

@echo off

set TESTDIRS=fast mid slow
set TD=src\test\resources\testsuite

for %%d in (%TESTDIRS%) do call :process_testdir %%d
goto :eof

:process_testdir
set CTD=%TD%\%1
echo CTD: %CTD%
    REM Echos the expected path

for /R %CTD% %%f in (*.fs) do (echo %%f)
    REM Echos as expected

goto :eof

I know GOTO is not very popular, but batch files were originally designed to use labels for control flow. The parenthetized control structure syntax was added later, and this question is an example of where it breaks down. The problem lends itself well to batch subroutines.

Solution 4

It's not obvious! It's the special parsing of FOR!
A FOR command is parsed directly after the escape/special character phase (for detecting the parenthesis), but as a result you can't using delayed or %%var expansion as parameters.

FOR %%a in (%%%%B) do (
  FOR %%a in (1) DO ( <<< this %%a will not replaced with %%B
      echo %%a - shows 1, because %%a is the name of the inner variable
      echo %%B - doesn't work
  )
)

And also this can't work:

set chars=abc
FOR /F "delims=!chars!" %%N in (bla) DO ....  

does not set a, b and c as delims, but !, c, h, a and r instead.

EDIT: Within the parentheses the delayed expansion does work as expected however:

set var=C:\temp
For %%a in (!var!) DO echo %%a

I would expect that you have to use a function to solve your problem.

Solution 5

Quote Malte Schwerhoff's Answer

If you don't want to repeat B, you can simply add "if" statement

@echo off
SetLocal

set B=alpha beta gamma
set A=eins zwo

FOR %%b in (%B%) do (
  FOR %%a in (%A% %%b) DO (
    IF %%b NEQ %%a (
        echo %%b -^> %%a
    )
  )
)

output:

alpha -> eins
alpha -> zwo
beta -> eins
beta -> zwo
gamma -> eins
gamma -> zwo
Share:
82,639

Related videos on Youtube

Malte Schwerhoff
Author by

Malte Schwerhoff

Enjoying computer science and cheese. Working as a lecturer at ETH Zurich, my research area is automating deductive software verification. See my university web page for further information.

Updated on July 09, 2022

Comments

  • Malte Schwerhoff
    Malte Schwerhoff almost 2 years

    The following nested for-loop drives me mad (on Windows 7):

    @echo off
    SetLocal EnableDelayedExpansion
    
    set TESTDIRS=fast mid slow
    set TD=src\test\resources\testsuite
    
    for %%d in (%TESTDIRS%) do (
        set CTD=%TD%\%%d
        echo CTD: !CTD!
            REM Echos the expected path
        echo CTD: %CTD%
            REM Echos nothing -- understandable
    
        for /R !CTD! %%f in (*.fs) do (echo %%f)
            REM Echos nothing -- why?
        for /R src\test\resources\testsuite\fast %%f in (*.fs) do (echo %%f)
            REM Echos expected files
    )
    

    I tried various solutions involving disabling DelayedExpansion, call-statements and whatnot, but I never got the inner loop working. I know that I could replace the inner loop by a subroutine call, but there gotta be a way to make it work with nested loops.

  • Malte Schwerhoff
    Malte Schwerhoff over 13 years
    Sorry, but I don't see how your reply helps me with my question since it only tells me what doesn't work. In addition, I already mentioned in my question that I am aware of the possibility of replacing the inner loop with a subroutine but my question was how to make the nested loop work.
  • Malte Schwerhoff
    Malte Schwerhoff over 13 years
    I'll accept this solution since it solves the question in the context of my example. And it's really clever :-)
  • jeb
    jeb over 13 years
    Sorry, but I suppose you can only use the PUSHD solution from Ben Voigt. With expansion it seems to be impossible, because the path for the inner loop is expanded only one, at the beginning of the first parenthesis,
  • Juan Antonio Orozco
    Juan Antonio Orozco over 9 years
    hi i used your example but it will print %%b instead of %%a in the last steep,i solved it by using just FOR %%a in (%A%) in the inner loop
  • Malte Schwerhoff
    Malte Schwerhoff almost 9 years
    @JuanAntonioOrozco You might have observed the intended behaviour. I edited my answer to clarify what the loop should print.
  • Algoman
    Algoman over 5 years
    This is a workaround that solves the specific problem differently. It doesn't explain how to correctly nest for-loops (i.e. doesn't answer the question). I want to nest for-loops with a different use-case in which this workaround doesn't solve the problem and came here via google search for "batch nested for loop". -1 from me.
  • John Neuhaus
    John Neuhaus almost 4 years
    This answer would include directories not in %TESTDIRS%. And as noted in the question, /R src\test\resources\testsuite\fast %%f works, which is the intended result of set CTD=%TD%\%%d, so the problem is not likely a misunderstanding of how the tree is being walked.