Nested batch for loops

2019-02-01 21:57发布

问题:

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.

回答1:

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



回答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).



回答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.



回答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.