2

I have a folder structure, which is like for example C:\Temp\ and there are a lot of folder and file, and within each folder there are a "callme.bat". I would like to create a so called main.bat which is one after another call the callme files within the main' window. But there is a problem, within the callme files are some echo which contains "!" mark what make a problem for me.

I realized the problem with the setlocal-endlocal combo, because the batch scrip wants to interpret the message within the "!" marks, so I must use endlocal, but if I did I not able to run the callme bats.

callme.bat

@echo off
echo !!! hidden message !!! not hidden message
pause

main.bat variant 1

@echo off
setlocal enabledelayedexpansion

set PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
    set CURR_DIR=%PATH%\%%x
    set ACTUAL_BATCH=!CURR_DIR!\callme.bat
    echo !ACTUAL_BATCH!

    call !ACTUAL_BATCH!
    pause
)
pause
exit

main.bat variant 2

@echo off
set PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
    setlocal enabledelayedexpansion

    set CURR_DIR=%PATH%\%%x
    set ACTUAL_BATCH=!CURR_DIR!\callme.bat
    echo !ACTUAL_BATCH!

    ENDLOCAL & SET VAR=!ACTUAL_BATCH!
    echo %VAR%
    pause
)
pause
exit

main.bat variant 3

@echo off
set PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
    setlocal enabledelayedexpansion

    set CURR_DIR=%PATH%\%%x
    set ACTUAL_BATCH=!CURR_DIR!\callme.bat
    echo !ACTUAL_BATCH!

    REM source: https://stackoverflow.com/questions/3262287/make-an-environment-variable-survive-endlocal
    for /f "delims=" %%A in (""!ACTUAL_BATCH!"") do endlocal & set "VAR=%%~A"
    echo %VAR%

    call %VAR%
    pause
)
pause
exit

So I don't know what to do. Anyone has an idea?

variant 1's output:

C:\Temp\1\callme.bat
 not hidden message
C:\Temp\2\callme.bat
 not hidden message

variant 2-3's output:

C:\Temp\1\callme.bat
ECHO is off.
C:\Temp\2\callme.bat
ECHO is off.
aschipfl
  • 33,626
  • 12
  • 54
  • 99
Gregori
  • 85
  • 7
  • 4
    Please do not redefine `%PATH%`, it is a very important system variable, change its name in every one of your `.bat` files. – Compo Jun 21 '19 at 12:30
  • 3
    You don't need to use delayed expansion or even `Set`. A single line script should work fine, `@For /F "Delims=" %%A In ('Dir /B/AD "C:\Temp" 2^>NUL')Do @Call "%%~fA\callme.bat" 2>NUL`, or alternatively, `@For /D %%A In ("C:\Temp\*")Do @Call "%%~fA\callme.bat" 2>NUL`. – Compo Jun 21 '19 at 12:52

2 Answers2

3

TL;DR

ENDLOCAL&set "varname=%sourcevarname%"

probably, where varname is the variablename to set and sourcevarname is the variable whose value is to be assigned to varname - and they CAN be the same name, even if the statement appears logically null - it's exporting the variable from within the setlocal/endlocal block.

Key point: MUST be on one physical line and may be repeated if necessary (ie

 ENDLOCAL&set "varname=%sourcevarname%"&set "varname2=%sourcevarname2%"

So

 ENDLOCAL&set "fred=%fred%"&set "bill=%george%"

is perfectly valid, to set the value of fred outside the setlocal/endlocal bracket to its final value inside and of billoutside to the final value of george inside.

Magoo
  • 77,302
  • 8
  • 62
  • 84
2

Some points about your code:

  1. Never use PATH as a variable name, as it destroys the PATH variable for searching executable files.

  2. Use the extended SET syntax set "varname=content" to avoid problems with trainling spaces.

  3. You only need to disable the delayed expansion mode by using setlocal DisableDelayedExpansion

@echo off
setlocal EnableDelayedExpansion

set MY_PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
    set "CURR_DIR=%MY_PATH%\%%x"
    set "ACTUAL_BATCH=!CURR_DIR!\callme.bat"

    call :execute ACTUAL_BATCH
    pause
)
pause
exit /b

:execute ACTUAL_BATCH
set "batFile=!%~1!"
echo Calling !batFile!

setlocal DisableDelayedExpansion
call %batFile%
endlocal

exit /b
jeb
  • 78,592
  • 17
  • 171
  • 225