Page 1 of 1

Vars over endlocal - return.bat / macro use?

Posted: 15 Feb 2016 17:02
by mirrormirror
So I have a question on overcoming the endlocal barrier/scope and the usage of tools like these:

a "return.bat" function here: viewtopic.php?t=6496
and a macro here: viewtopic.php?f=3&t=6796&p=44199#p44199

Forgive me if this is too basic but could someone give me a few lines of code to demonstrate how one would use these in a real world example? Here is a some sample code where I want to get some variables out of a FOR loop where the scope changes. The code works but could I use either one of these (return.bat or the return function) to accomplish this? If so, can someone show me how?

Code: Select all

SetLocal EnableDelayedExpansion
SET "DatasetFlag=::::Dataset_Users"
   SET "$="
   FOR /F "usebackq tokens=1,* delims==" %%a IN (`SET usrs_`) DO SET "%%a="
   FOR /F "usebackq tokens=* delims=" %%_ IN ("%~f0") DO (
     IF /I "%%_"=="%DatasetFlag%_End" SET "$="
      REM NEED EnableDelayedExpansion for the nexe line
        IF "!$!" neq "" (
         REM NEED DisableDelayedExpansion for !characters! in passwords
         SetLocal DisableDelayedExpansion
         FOR /F "usebackq tokens=1,2,3,* delims=/" %%a IN (`@ECHO %%_`) DO (
            rem @ECHO a: %%a - b: %%b   - c: %%c   d: %%d
            net user %%a %%d /add /y
            net localgroup %myusergroup% %%a /add
            IF NOT EXIST "%UserSystems%\%%c.ro" MD "%UserSystems%\%%c.ro"
               NET SHARE "%%c.ro$"="%UserSystems%\%%c.ro" /GRANT:Administrators,FULL /GRANT:%%a,READ
            REM NEED to get this past endlocal: CALL SET "%%b/%%a=%%a"
            CALL SET "%%b/%%a=%%a" &EndLocal
            SetLocal EnableDelayedExpansion
               CALL SET "%%b/%%a=%%a"
               FOR /F "delims=" %%Z IN ('SET %%b/%%a') do EndLocal &SET %%Z
         )
      )
     IF "%%_"=="%DatasetFlag%_Begin" SET "$=1"
   )
   REM this could have been done in main loop but I forced it here for this example
   FOR /F "usebackq tokens=1,2,* delims==/" %%a IN (`SET usrs_`) DO (
      net localgroup %%a %%c /add
   )
EndLocal
GOTO:ENDScript

::::Dataset_Users_Begin
testuser1//usrs_LAN//test_computer1//"pass_!^^test"
testuser8//usrs_WAN//computer8//"bad!password!"
testuser9//usrs_LAN//computer10//"ab\c/H/ello!_&_^^!!"
::::Dataset_Users_End

Re: Vars over endlocal - return.bat / macro use?

Posted: 15 Feb 2016 18:05
by aGerman
I have a question on overcoming the endlocal barrier/scope

If you're acting in a block of command lines you can use FOR /F. FOR variables are valid even if ENDLOCAL was executed.

Code: Select all

@echo off
set "x=a"
echo %x%
(
  setlocal EnableDelayedExpansion
  set "x=b"
  echo !x!
  for /f "delims=" %%i in ("!x!") do (
    endlocal
    echo %%i
    echo %x%
  )
)
pause


But ...
IF "!$!" neq "" (

... is the same as ...

Code: Select all

if defined $ (

No need for delayed expansion at this point.

Regards
aGerman

Re: Vars over endlocal - return.bat / macro use?

Posted: 15 Feb 2016 18:19
by mirrormirror
But ...
Quote:
IF "!$!" neq "" (

... is the same as ...
Code:

if defined $ (


No need for delayed expansion at this point.

So then this:

Code: Select all

IF "%%_"=="%DatasetFlag%_Begin" SET "$=1"

would only matter if, for example I re-SET "$=" ?

But back to my question - can you provide an example of how one would use the return.bat or the other macro in a simple scenario?

Re: Vars over endlocal - return.bat / macro use?

Posted: 15 Feb 2016 18:32
by aGerman
No, sorry. That seems to be some undefined behavior of GOTO. I fear the macro technique has actually nothing to do with that topic.

Re: Vars over endlocal - return.bat / macro use?

Posted: 16 Feb 2016 12:47
by jeb
mirrormirror wrote:But back to my question - can you provide an example of how one would use the return.bat or the other macro in a simple scenario?


At SO:preserving exclamation marks in variable between setlocals batch is a good example why you need it and how to use it.

Or think of a function, that adds one exclamation mark to a string

Code: Select all

@echo off
set "endlocal="
setlocal DisableDelayedExpansion
call :init

setlocal EnableDelayedExpansion
set "testVar=Hello"

call :AddBang testVar
set testVar
%endlocal% testVar

setlocal DisableDelayedExpansion
call :AddBang testVar
set testVar

%endlocal% testVar
set testVar

exit /b

:AddBang
setlocal EnableDelayedExpansion
set "%~1=!%~1!^!"
echo %~1 !%~1!
%endlocal% %~1
exit /b

setlocal ENABLEEXTENSIONS

:init
set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
%=   I use EDE for EnableDelayeExpansion and DDE for DisableDelayedExpansion =%
set ^"endlocal=for %%# in (1 2) do if %%#==2 (%\n%
   setlocal EnableDelayedExpansion%\n%
 %=       Take all variable names into the varName array       =%%\n%
   set varName_count=0%\n%
   for %%C in (!args!) do set "varName[!varName_count!]=%%~C" ^& set /a varName_count+=1%\n%
 %= Build one variable with a list of set statements for each variable delimited by newlines =%%\n%
 %= The lists looks like --> set result1=myContent\n"set result1=myContent1"\nset result2=content2\nset result2=content2\n     =%%\n%
 %= Each result exists two times, the first for the case returning to DDE, the second for EDE =%%\n%
 %= The correct line will be detected by the (missing) enclosing quotes  =%%\n%
   set "retContent=1!LF!"%\n%
   for /L %%n in (0 1 !varName_count!) do (%\n%
      for /F "delims=" %%C in ("!varName[%%n]!") DO (%\n%
         set "content=!%%C!"%\n%
         set "retContent=!retContent!"set !varName[%%n]!=!content!"!LF!"%\n%
         if defined content (%\n%
 %=      This complex block is only for replacing '!' with '^!'      =%%\n%
 %=    First replacing   '"'->'""q'   '^'->'^^' =%%\n%
         set ^"content_EDE=!content:"=""q!"%\n%
         set "content_EDE=!content_EDE:^=^^!"%\n%
 %= Now it's poosible to use CALL SET and replace '!'->'""e!' =%%\n%
         call set "content_EDE=%%content_EDE:^!=""e^!%%"%\n%
         %= Now it's possible to replace '""e' to '^', this is effectivly '!' -> '^!'  =%%\n%
         set "content_EDE=!content_EDE:""e=^!"%\n%
         %= Now restore the quotes  =%%\n%
         set ^"content_EDE=!content_EDE:""q="!"%\n%
         ) ELSE set "content_EDE="%\n%
         set "retContent=!retContent!set "!varName[%%n]!=!content_EDE!"!LF!"%\n%
      )%\n%
   )%\n%
 %= Now return all variables from retContent over the barrier =%%\n%
   for /F "delims=" %%V in ("!retContent!") DO (%\n%
 %= Only the first line can contain a single 1 =%%\n%
      if "%%V"=="1" (%\n%
 %= We need to call endlocal twice, as there is one more setlocal in the macro itself =%%\n%
         endlocal%\n%
         endlocal%\n%
      ) ELSE (%\n%
 %= This is true in EDE             =%%\n%
         if "!"=="" (%\n%
            if %%V==%%~V (%\n%
               %%V !%\n%
            )%\n%
         ) ELSE IF not %%V==%%~V (%\n%
            %%~V%\n%
         )%\n%
      )%\n%
   )%\n%
 ) else set args="

exit /b

Re: Vars over endlocal - return.bat / macro use?

Posted: 21 Feb 2016 18:31
by mirrormirror
Thank you jeb - this helps.

Any other examples that anyone else can think of are welcome. I'm always looking for new ideas.

Re: Vars over endlocal - return.bat / macro use?

Posted: 28 Jul 2016 00:36
by mirrormirror
At SO:preserving exclamation marks in variable between setlocals batch is a good example why you need it and how to use it.
Link: http://stackoverflow.com/questions/29869394/preserving-exclamation-marks-in-variable-between-setlocals-batch

Hi jeb, I have a question on the macro you linked over @SO (thank you so much for sharing the code).

1. I'm assuming it does not handle multi-line variables - i.e. containing linefeeds. correct? And if so, is there any way to do this. Here is a test using your macro to show what I mean:

Code: Select all

@ECHO OFF &SETLocal DisableDelayedExpansion
REM Usage: %endlocal% var1 var2 var3 var4 var5
REM    Note: this macro performs an "ENDLocal" command - keep watch on SETLocal / ENDLocal pairs

set aaa=
set bbb=

set LF=^



set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
%=   I use EDE for EnableDelayeExpansion and DDE for DisableDelayedExpansion =%
set ^"endlocal=for %%# in (1 2) do if %%#==2 (%\n%
   setlocal EnableDelayedExpansion%\n%
 %=       Take all variable names into the varName array       =%%\n%
   set varName_count=0%\n%
   for %%C in (!args!) do set "varName[!varName_count!]=%%~C" ^& set /a varName_count+=1%\n%
 %= Build one variable with a list of set statements for each variable delimited by newlines =%%\n%
 %= The lists looks like --> set result1=myContent\n"set result1=myContent1"\nset result2=content2\nset result2=content2\n     =%%\n%
 %= Each result exists two times, the first for the case returning to DDE, the second for EDE =%%\n%
 %= The correct line will be detected by the (missing) enclosing quotes  =%%\n%
   set "retContent=1!LF!"%\n%
   for /L %%n in (0 1 !varName_count!) do (%\n%
      for /F "delims=" %%C in ("!varName[%%n]!") DO (%\n%
         set "content=!%%C!"%\n%
         set "retContent=!retContent!"set !varName[%%n]!=!content!"!LF!"%\n%
         if defined content (%\n%
 %=      This complex block is only for replacing '!' with '^!'      =%%\n%
 %=    First replacing   '"'->'""q'   '^'->'^^' =%%\n%
         set ^"content_EDE=!content:"=""q!"%\n%
         set "content_EDE=!content_EDE:^=^^!"%\n%
 %= Now it's poosible to use CALL SET and replace '!'->'""e!' =%%\n%
         call set "content_EDE=%%content_EDE:^!=""e^!%%"%\n%
         %= Now it's possible to replace '""e' to '^', this is effectivly '!' -> '^!'  =%%\n%
         set "content_EDE=!content_EDE:""e=^!"%\n%
         %= Now restore the quotes  =%%\n%
         set ^"content_EDE=!content_EDE:""q="!"%\n%
         ) ELSE set "content_EDE="%\n%
         set "retContent=!retContent!set "!varName[%%n]!=!content_EDE!"!LF!"%\n%
      )%\n%
   )%\n%
 %= Now return all variables from retContent over the barrier =%%\n%
   for /F "delims=" %%V in ("!retContent!") DO (%\n%
 %= Only the first line can contain a single 1 =%%\n%
      if "%%V"=="1" (%\n%
 %= We need to call endlocal twice, as there is one more setlocal in the macro itself =%%\n%
         endlocal%\n%
         endlocal%\n%
      ) ELSE (%\n%
 %= This is true in EDE             =%%\n%
         if "!"=="" (%\n%
            if %%V==%%~V (%\n%
               %%V !%\n%
            )%\n%
         ) ELSE IF not %%V==%%~V (%\n%
            %%~V%\n%
         )%\n%
      )%\n%
   )%\n%
 ) else set args="


SETLocal DisableDelayedExpansion
@ECHO(
@ECHO BEFORE MACRO -------------------
SET aaa=1
set bbb=1%\n%
. . . 2%\n%
. . . . . . 3

SET aaa
SET bbb

@ECHO(
@ECHO AFTER MACRO -------------------
%endlocal% bbb
SET aaa
SET bbb

@ECHO(
@ECHO AFTER MACRO and AFTER endlocal -------------------
endlocal
SET aaa
SET bbb
OUTPUT:

Code: Select all

BEFORE MACRO -------------------
aaa=1
bbb=1
. . . 2
. . . . . . 3

AFTER MACRO -------------------
Environment variable aaa not defined
bbb=1

AFTER MACRO and AFTER endlocal -------------------
Environment variable aaa not defined
Environment variable bbb not defined
END ------------------------------------------------
In the "After Macro" label - it only reads "bbb=1" rather than all three original lines. Is there a way to preserve all three lines of the variable when crossing over endlocal?

Re: Vars over endlocal - return.bat / macro use?

Posted: 28 Jul 2016 02:34
by foxidrive
Google is a great way to search dostips:

https://www.google.com.au/#q=preserve+v ... ostips.com

Re: Vars over endlocal - return.bat / macro use?

Posted: 28 Jul 2016 02:52
by jeb
mirrormirror wrote:1. I'm assuming it does not handle multi-line variables - i.e. containing linefeeds. correct? And if so, is there any way to do this.


Hi mirrormirror,

yes, you are right, a macro can't handle linefeeds and carriage returns.
But with a helper function even this can be done.

As always, it's already posted at Dostips - [Solved] Return ANY string across ENDLOCAL boundry - BUG!

That isn't the macro version, as it was developed before the macro technic was born.
Soo, I will complete the macro code at SO:

Re: Vars over endlocal - return.bat / macro use?

Posted: 28 Jul 2016 14:56
by mirrormirror
Soo, I will complete the macro code at SO:
Awesome! Thank you - I will be looking forward to it :)