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=6496and a macro here:
viewtopic.php?f=3&t=6796&p=44199#p44199Forgive 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 ...
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-batchHi 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
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