Discussion forum for all Windows batch related topics.
Moderator: DosItHelp
-
jeb
- Expert
- Posts: 1055
- Joined: 30 Aug 2007 08:05
- Location: Germany, Bochum
#16
Post
by jeb » 12 Dec 2011 07:46
Ok, it's obvious: I didn't test it before
But also that the brackets are unbalanced
That's the test against newbies
It should be
Code: Select all
set LF=^
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set $SomeMacro=for /L %%n in (1 1 2) do if %%n==2 (%\n%
set argv%\n%
for /f "delims=" %%r in ("!argv!") do endlocal^& echo.set "%%~3=%%~r" %\n%
) ELSE setlocal enableDelayedExpansion ^& echo ProofOfconcept is showed only once %%n ^& set argv=,
%$SomeMacro% a,b,c
Output wrote:ProofOfconcept is showed only once 1
argv=, a,b,c
set "%~3=, a,b,c"
jeb
-
Ed Dyreen
- Expert
- Posts: 1569
- Joined: 16 May 2011 08:21
- Location: Flanders(Belgium)
-
Contact:
#17
Post
by Ed Dyreen » 12 Dec 2011 07:53
'
I see, but to get oopsy at the bottom you still need bracket
Code: Select all
@echo off
set LF=^
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set $SomeMacro=for /L %%n in (1 1 2) do if %%n==2 (%\n%
set argv%\n%
for /f "delims=" %%r in ("!argv!") do endlocal^& echo.set "%%~3=%%~r" %\n%
) ELSE setlocal enableDelayedExpansion ^& echo ProofOfconcept is showed only once %%n ^& set argv=,
%$SomeMacro% a,b,c &echo.oopsy
pause
exit
Code: Select all
ProofOfconcept is showed only once 1
oopsy
argv=, a,b,c
set "%~3=, a,b,c "
Druk op een toets om door te gaan. . .
Code: Select all
@echo off
set LF=^
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set $SomeMacro=for /L %%n in (1 1 2) do if %%n==2 (%\n%
set argv%\n%
for /f "delims=" %%r in ("!argv!") do endlocal^& echo.set "%%~3=%%~r" %\n%
) ELSE setlocal enableDelayedExpansion ^& echo ProofOfconcept is showed only once %%n ^& set argv=,
( %$SomeMacro% a,b,c ) &echo.oopsy
pause
exit
Code: Select all
ProofOfconcept is showed only once 1
argv=, a,b,c
set "%~3=, a,b,c "
oopsy
Druk op een toets om door te gaan. . .
-
Aacini
- Expert
- Posts: 1913
- Joined: 06 Dec 2011 22:15
- Location: México City, México
-
Contact:
#18
Post
by Aacini » 12 Dec 2011 21:55
jeb wrote:Welcome Aacini to the expert discussions
Thanks, jeb.
Aacini wrote:Because this trick use textual substitution of whichever text placed after the macro, if another & COMMAND is placed after macro params, that command will also be carried into the for body and hence also executed 2 times:
[...]
The only way to avoid this effect is isolating the macro and parameters from the rest of the line by enclosing they in parentheses, as Ed indicated.
Did I said that I love to disprove such declarations
I simply forgot to post the solution for this
Code: Select all
set $SomeMacro=for /L %%n in (1 1 2) do if %%n==2 (%\n%
rem *** MACRO Content
for /f "delims=" %%r in ("!result!") do endlocal^& set "%%~3=%%~r" %\n%
) %\n%
) ELSE setlocal enableDelayedExpansion ^& echo ProofOfconcept is showed only once %%n ^& set argv=,
In this case it is really obvious, isn't it?
jeb
Although you are right, we are talking about two different things.
All my description is based on your first example, where the FOR command have parentheses to enclose its body:
jeb wrote:The main idea is this:
Code: Select all
FOR/L %%n in (1,1,2) DO (
if %%n==2 ( // Now we can access the parameters
// get the parameters
// run the macro
)
) & set parameters=
And here is a working sample...
Code: Select all
[...]
set $strLen=for /L %%n in (1 1 2) do ( %\n%
if %%n==2 (%\n%
[...]
jeb
When dbenham noted that ...
jeb wrote:dbenham wrote:Two minor inconveniences: [...]
2) Any command added on the same line after the macro call will be executed twice
The solution for these two points is obviously
- Do the set argv and the rest only if %%n==1
Code: Select all
[...]
set argv=original
set $strLen=for /L %%n in (1 1 2) do if %%n==2 (%\n%
[...]
[...]
jeb
... you "forgot" to mention that, in order for this method to work, the parentheses of the FOR body
MUST be deleted
I didn't realized that these parentheses were removed until now. This change mades my description about this problem totally nonsense, including the FOR with 3 IF's example. I must modify these parts in my explanation...
Antonio
-
Aacini
- Expert
- Posts: 1913
- Joined: 06 Dec 2011 22:15
- Location: México City, México
-
Contact:
#19
Post
by Aacini » 15 Dec 2011 22:46
jeb: I modified my original answer as I said. Perhaps you would like to review it looking for a new declaration to disprove
-
jeb
- Expert
- Posts: 1055
- Joined: 30 Aug 2007 08:05
- Location: Germany, Bochum
#20
Post
by jeb » 18 Dec 2011 17:01
@Aacini: The explanation looks fine
Currently I haven't an idea to solve the & append command problem, so the command will be executed after the rest of the macro.
Pipes have the required precedence, but they seem to be unsuitable for a solution here.
Code: Select all
macro .... | set argv= .... & echo This executes at the end
( echo 1 & echo 2 ) | more & echo end
jeb
-
Aacini
- Expert
- Posts: 1913
- Joined: 06 Dec 2011 22:15
- Location: México City, México
-
Contact:
#21
Post
by Aacini » 27 Dec 2011 00:28
Ed Dyreen wrote:And while you're at it, how about:
Code: Select all
set /a $var=%StrLen% ( "I wish this worked" ) + %random%
Now that would be really impressive.
Yes, it can be done now this way:
Code: Select all
set "string=I wish this worked"
%set/a% $var=StrLen(string)+%random%
See my SET/A macro at
this topic
-
mirrormirror
- Posts: 129
- Joined: 08 Feb 2016 20:25
#22
Post
by mirrormirror » 28 Jul 2016 19:39
I'm working to get my head around macros and just wanted to ask if this thread contained the most recent developments / syntax conventions that you guys use. In particular, I'm relying on this post:
http://www.dostips.com/forum/viewtopic.php?f=3&t=2518#p12045 and hoping the info. is up to date.
I've read through newer threads discussing techniques to add comments to macros but they seemed to use the same conventions as discussed here.
I'm trying to write a couple of macros and just want to use the latest and greatest methods if possible.
btw: a special thanks to Aacini for taking the time to document this in detail. A question on your post though... Was there supposed to be a code example in here - right before the "Second Part:..." ?
Code: Select all
However, because this method use textual substitution of whichever text placed after the macro, if another & COMMAND is placed after macro params, then that command also belongs to ELSE part and will also be executed before the macro, that is an unexpected behavior. The only way to avoid this effect is isolating the macro and parameters from the rest of the line by enclosing they in parentheses.
Second part: An additional contribution.
When we define a macroFuncWithParams variable with this format, we may duplicate the original macroFunc code in the macro execution part, taking the parameters from argv variable (as in jeb example above); or we may use the original macroFunc code over argv variable in a nested macro invocation:
-
mirrormirror
- Posts: 129
- Joined: 08 Feb 2016 20:25
#23
Post
by mirrormirror » 29 Jul 2016 16:56
Also, Aacini, when I try your code from this post:
http://www.dostips.com/forum/viewtopic.php?f=3&t=2518#p12045 it doesn't work - i.e. my output differs from what you indicated in your post:
your code:
Code: Select all
@echo off
cls
setlocal DisableDelayedExpansion
set LF=^
::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set strLen=(%\n%
setlocal enableDelayedExpansion%\n%
set "str=A!%%~b!"%\n%
set "len=0"%\n%
for /l %%A in (12,-1,0) do (%\n%
set /a "len|=1<<%%A"%\n%
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"%\n%
)%\n%
for %%v in (!len!) do endlocal^&if "%%~a" neq "" (set "%%~a=%%v") else echo %%v%\n%
)
set let=for %%n in (1 2) do if %%n==2 (%\n%
for /F "tokens=1-4 delims==(,) " %%1 in ("!argv!") do (%\n%
echo -------------------------- %\n%
set macroFunc=%%1%\n%
echo macroFunc=!macroFunc! %\n%
set param1=%%2%\n%
echo param1=!param1! %\n%
REM set param2=%%3%\n%
REM echo param2=!param2! %\n%
set resultVar=%%3%\n%
echo resultVar=!resultvar! %\n%
call !macroFunc! !param1! !param2! resultValue%\n%
REM for /F "tokens=1-26" %%a in ("!param1! !param2! resultValue") do (%\n%
REM nested call %%%%macroFunc%%%% %\n%
REM )%\n%
for %%v in (!resultValue!) do endlocal^&set "%%1=%%v"%\n%
echo -------------------------- %\n%
)%\n%
) else setlocal enableDelayedExpansion ^& set argv=
set "testString=this has a length of 23"
echo Test string is ("%testString%")
echo/
echo Original (FOR /F ...) strLen macro invocation:
for /F "tokens=1-26" %%a in ("testString forResult") do %strLen%
echo Original FOR macro invocation result is %forResult%
echo/
echo Original "CALL :strLen testString subResult" subroutine invocation:
call :strLen testString subResult=
echo Original CALL subroutine invocation result is %subResult%
echo/
echo %%Let%% letResult=:strLen(testString) invocation:
%let% letResult=:strLen(testString)
echo LET letResult=strLen(testString) result is %letResult%
goto :EOF
:strLen string resultVar=
setlocal enableDelayedExpansion
set "str=0!%~1!"
set "len=0"
for /l %%A in (12,-1,0) do (
set /a "len|=1<<%%A"
for %%B in (!len!) do if "!str:~%%B,1!"=="" set /a "len&=~1<<%%A"
)
for %%v in (!len!) do endlocal&if "%~2" neq "" (set "%~2=%%v") else echo %%v
exit /B
Your Output:
Code: Select all
Test string is ("this has a length of 23")
Original (FOR /F ...) strLen macro invocation:
Original FOR macro invocation result is 23
Original "CALL :strLen testString subResult" subroutine invocation:
Original CALL subroutine invocation result is 23
%Let% letResult=:strLen(testString) invocation:
--------------------------
macroFunc=:strLen
param1=testString
resultVar=letResult
--------------------------
LET letResult=strLen(testString) result is 23
My Output:
Code: Select all
Test string is ("this has a length of 23")
Original (FOR /F ...) strLen macro invocation:
Original FOR macro invocation result is
Original "CALL :strLen testString subResult" subroutine invocation:
Original CALL subroutine invocation result is 1
%Let% letResult=:strLen(testString) invocation:
--------------------------
macroFunc=letResult
param1=:strLen
resultVar=testString
'letResult' is not recognized as an internal or external command,
operable program or batch file.
--------------------------
LET letResult=strLen(testString) result is
-
jeb
- Expert
- Posts: 1055
- Joined: 30 Aug 2007 08:05
- Location: Germany, Bochum
#24
Post
by jeb » 31 Jul 2016 05:31
Hi mirrormirror,
yes, I suppose it's more or less the state of art for macro programming.
-
aGerman
- Expert
- Posts: 4678
- Joined: 22 Jan 2010 18:01
- Location: Germany
#26
Post
by aGerman » 06 Aug 2018 11:32
Do you remember what the caret at the end was good for if you read that, Dave?
viewtopic.php?f=3&t=1827#p7358
I tried to follow that old thread but with all these double and triple escapings I'm not able to recognize it anymore
Steffen
-
dbenham
- Expert
- Posts: 2461
- Joined: 12 Feb 2011 21:02
- Location: United States (east coast)
#27
Post
by dbenham » 06 Aug 2018 12:19
Sure.
The old \n was defined as ^^^<LF><LF>^<LF><LF>^^
After parsing the definition, the actual stored value is ^<LF><LF>^
When included at the end of a macro definition line, the ^<LF><LF> inserts a single <LF> into the macro definition, and the trailing ^ escapes the (<CR>)<LF> at the end of the source code line so that the next source line is included in the definition. The first character of that next line is escaped during the definition of the macro - But that typically doesn't matter.
The new \n is defined as ^^^(<CR>)<LF>(<CR>)<LF>
After parsing the definition, the actual stored value is ^<LF>
When included at the end of a macro definition line, the ^<LF> is in front of the (<CR>)<LF> at the end of the source line, which is parsed as a single <LF> that gets inserted into the definition, and the next line is still appended to the definition. This time the first character of the next line is not escaped during the definition - Again, this typically does not matter.
Dave Benham
-
aGerman
- Expert
- Posts: 4678
- Joined: 22 Jan 2010 18:01
- Location: Germany
#28
Post
by aGerman » 06 Aug 2018 13:10
dbenham wrote: ↑06 Aug 2018 12:19
Again, this typically does not matter.
That's what I wasn't clear anymore
I just wanted to make sure there was no exception where escaping the first character would have been required.
Steffen
-
dbenham
- Expert
- Posts: 2461
- Joined: 12 Feb 2011 21:02
- Location: United States (east coast)
#29
Post
by dbenham » 06 Aug 2018 15:25
No, that was a side effect rather than a goal. We mistakenly thought the \n definition required ^<LF><LF>, meaning the (<CR>)<LF> at the end of the line would normally terminate the definition. The trailing ^ was just to force the next line to be appended to the definition.
The escaped first character of the next line is only during the definition of the macro. So if the first character of the next line happens to be a special character that normally requires escaping, then the old definition avoids the need to explicitly escape it. The only case I can think of where this makes sense in a macro definition is if you put redirection at the front of a command.
I also often get confused between the escapes in the definition vs the execution of the macro.
In a previous post I think I said there could be an advantage of the new \n, but now I don't see any.
Dave Benham
-
Ed Dyreen
- Expert
- Posts: 1569
- Joined: 16 May 2011 08:21
- Location: Flanders(Belgium)
-
Contact:
#30
Post
by Ed Dyreen » 19 Jan 2019 07:21
dbenham wrote: ↑06 Aug 2018 12:19
Sure.
The old \n was defined as ^^^<LF><LF>^<LF><LF>^^
After parsing the definition, the actual stored value is ^<LF><LF>^
When included at the end of a macro definition line, the ^<LF><LF> inserts a single <LF> into the macro definition,
and the trailing ^ escapes the (<CR>)<LF> at the end of the source code line so that the next source line is included in the definition. The first character of that next line is escaped during the definition of the macro - But that typically doesn't matter.
The new \n is defined as ^^^(<CR>)<LF>(<CR>)<LF>
After parsing the definition, the actual stored value is ^<LF>
When included at the end of a macro definition line, the ^<LF> is in front of the (<CR>)<LF> at the end of the source line, which is parsed as a single <LF> that gets inserted into the definition, and the next line is still appended to the definition. This time the first character of the next line is not escaped during the definition - Again, this typically does not matter.
Dave Benham
It does matter if the first character of that next line is escaped during the definition of the macro. namely if the character that is escaped is " then the quote effect will be disabled.
Code: Select all
set ^"x= for (1,2) if 2 ( %\n%
%\n%
%==%for ( %\n%
%= =%"x" %\n%
%==%) do 2 %\n%
%\n%
) else set 2="
I guess do was unexpected at the time... But easily avoided:
Code: Select all
set ^"x= for (1,2) if 2 ( %\n%
%\n%
%==%for ("x") do 2 %\n%
%\n%
) else set 2="
all characters that are affected by escaping > | & < cannot follow %\n% directly so inside macros
Code: Select all
set ^"x= for (1,2) if 2 ( %\n%
%\n%
%==%(echo.this works)^>nul%\n%
%\n%
) else set 2="
Code: Select all
set ^"x= for (1,2) if 2 ( %\n%
%\n%
%==%^>nul (echo.but this fails)%\n%
%\n%
) else set 2="