Search code examples
windowsbatch-filecmdcallcommand-prompt

Receive variable from a called batch file


I have two batch file here, test.bat and len.bat.

len.bat is a function that receives input from test.bat, processes it and then return a result value back to test.bat.

test.bat

@echo off
setlocal EnableDelayedExpansion
call len tesla
echo !result!
pause

len.bat

@echo off
setlocal EnableDelayedExpansion
set "string=%~1"
for /l %%a in (0,1,10000) do if "!string:~%%a,1!" == "" (
    set result=%%a
    exit /b
)

When I open test.bat, I expect it would print a value. Instead, it says Echo is OFF. There seem to be a problem passing the variable from len.bat to test.bat.


Solution

  • setlocal can be thought of as a sandbox, and anything created inside of that sandbox exists for as long as the sandbox exists. The sandbox stops existing when endlocal is called - either explicitly, or implicitly when the script ends.

    In len.bat, you add a sandbox inside of the sandbox with a second setlocal enabledelayedexpansion command, and !result! is created inside of that inner sandbox. When len.bat ends, !result! is destroyed since it didn't exist before len.bat was called.

    I suspect that you added the second setlocal enabledelayedexpansion because you thought it was needed to use delayed expansion inside of len.bat, but delayed expansion is actually still enabled because it was turned on in test.bat.

    Delete the setlocal enabledelayedexpansion from len.bat and test.bat will correctly return "5".

    If you feel that you need to keep the setlocal enabledelayedexpansion in len.bat to use it separately from test.bat, you can explicitly call endlocal and chain the set command to it to trick the interpreter into letting the variable escape the sandbox.

    @echo off
    setlocal EnableDelayedExpansion
    set "string=%~1"
    for /l %%a in (0,1,10000) do if "!string:~%%a,1!" == "" (
        endlocal & set result=%%a
        exit /b
    )