Macros with parameters appended

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Macros with parameters appended

#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:

Re: Macros with parameters appended

#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: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Macros with parameters appended

#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 :?: 8)

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 :wink:

- 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: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Macros with parameters appended

#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: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Macros with parameters appended

#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: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Macros with parameters appended

#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. :lol:


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

Re: Macros with parameters appended

#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

Re: Macros with parameters appended

#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: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Macros with parameters appended

#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.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Macros with parameters appended

#25 Post by dbenham » 05 Aug 2018 19:49

The \n value used to create multi-line macros can be greatly simplified as ^<LF> instead of ^<LF><LF>^.

Code: Select all

(set \n=^^^
%= This creates an escaped Line Feed - DO NOT ALTER =%
)

Dave Benham

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Macros with parameters appended

#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)

Re: Macros with parameters appended

#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: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Macros with parameters appended

#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 :wink: 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)

Re: Macros with parameters appended

#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:

Re: Macros with parameters appended

#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="

Post Reply