views:

85

answers:

2

I frequently find myself using setlocal within cmd.exe to avoid polluting the environment variable space with temporary variables (and to ensure both command extensions and delayed expansion are active).

However, I'm at a loss on how to do this if I actually want one of those variables to be made available.

Consider the following code which gives you the final component of the current directory (so c:\pax\dir1 would give you dir1):

        @echo off
    :main
        setlocal enableextensions enabledelayedexpansion
        call :func
        echo.main_folder = %folder%
        endlocal
        goto :eof

    :func
        setlocal enableextensions enabledelayedexpansion
        set temp=%cd%
        set folder=
    :funcloop1
        if not "x%temp:~-1%"=="x\" (
            set folder=!temp:~-1!!folder!
            set temp=!temp:~1,-1!
            goto :funcloop1
        )
        echo.func_folder = %folder%
        endlocal
        goto :eof

When I run this, I get the output:

func_folder = dir1
main_folder =

and you can see that the %folder% variable does not survive the endlocal. However, if I leave off the setlocal/endlocal from the function, the temp varaible pollutes the namespace of main.

I know I can just use set temp= at the end of func and this will remove the environment variable but that doesn't cover the case where I'm already using that variable in the outside scope (it gets totally destroyed in that case).

Is there a way in cmd.exe to allow a select group of environment variables to propogate to the outside scope while still preventing others from affecting it? In other words, can you have both local variables and return variables?


Aside, please don't tell me there's a better way of getting the final path component. That is an example case only.

Actually, you can tell me a better way since it'd be nice to know but it won't be answering my specific question :-)

+2  A: 

"The solution to this is to take advantage of the fact that the CMD shell evaluates variables on a line-by-line basis - so placing ENDLOCAL on the same line as the SET statement(s) gives the result we want:" source: ss64.com

So changing the endlocal of func: to this...

endlocal & set folder=%folder%

...will get you the behaviour you want.

Robert
With maybe some more parenthesis thrown in?
Ben Voigt
@Ben Not to my knowledge. The ampersand, as used, is a command separator not an operator. Not that using parenthesis would hurt, so if you'd prefer feel free.
Robert
paxdiablo
Sorry, I meant to say quotes, not parentheses.
Ben Voigt
A: 

For better reading and formatting, you can use parentheses.

call :func resultVar
echo(%resultVar%
goto :eof

:func 
setlocal
set var=2
(
  endlocal
  if "%1" NEQ "" set "%1=%var%"
  goto :eof
)

This works, because a command block will be expanded like a single line. The parser expands the first two phases (percent variables and escape characters) before executing the command block. The other phases are executed in time (Like delayed expansion, second expand phase by CALL, ...)

jeb