Matt Williamson
Matt Williamson

Reputation: 7095

Read second parameter of a function into an array

I wrote the following code that works exactly the way I want:

@echo off
setlocal enabledelayedexpansion

set /a i=0
set in="This is line 1", "This is line 2"
for %%h in (%in%) do (
   set /a i+=1
   set "val[!i!]=%%h"
)
set out=
for /l %%n in (1,1,!i!) do (
  set out=!out! !val[%%n]! ^& vbcrlf ^& _)
echo !out:~1,-12!

Which takes the value of the %in% variable and reads each comma separated line into an element of an array and then does some string concatenation on it and spits out a new string. Now, when I try to turn it into a function, it fails because of how %2 is being parsed as an argument. I need %2 to be parsed as a single, comma separated string with a variable amount of values. This simple test doesn't work:

call :Test Title "This is line 1","This is line 2" "arg3"
exit /b

:Test arg1 arg2 arg3
set /a i=0
for %%h in (%2) do (
   set /a i+=1
   set "val[!i!]=%%h"
)
set out=
for /l %%n in (1,1,!i!) do (
  set out=!out! !val[%%n]! ^& vbcrlf ^& _)
echo %1 !out:~1,-12! %3
exit /b

The only thing I can think of is to use %* and change the delimiter to something unique but I'd rather avoid that if possible.

Upvotes: 2

Views: 269

Answers (2)

dbenham
dbenham

Reputation: 130819

For a general solution, passing values by reference (store value in a variable and pass variable name) is the best option. This is the same as David Ruhmann's second option.

There is another way, but it requires more work by the caller. You can require that all quotes in the parameter value be doubled up, and then enclose the entire parameter in one more set of quotes. Within the function, replace all "" with " to get the desired value. I used to use this method until I learned about passing values by reference.

@echo off
setlocal enableDelayedExpansion
call :Test Title """This is line 1"",""This is line 2""" "arg3"
exit /b

:Test arg1 arg2 arg3
set "arg2=%~2"
set "arg2=%arg2:""="%"
echo arg1=%1
echo arg2=%arg2%
echo arg3=%3

UPDATE

Passing values by reference is the best option to pass complex values that contain token delimiters and quotes.

But the OP isn't really interested in the list of values as a single parameter, since a FOR loop is used to split them up. The FOR loop can run into trouble if any of the values contain * or ?.

I now see that for this particular case, it is better to move the list to the end such that all arguments from 3 on are part of the list. Then use SHIFT /3 and a GOTO loop to read in the list values. This is basically David Ruhmann's option 1.

Upvotes: 2

David Ruhmann
David Ruhmann

Reputation: 11367

1. Shift through the Middle parameters

This will leave %1 alone and shift though all the middle parameters stopping when there are no more and leaving the last param in %3.

@echo off
setlocal
call :Test One "Two","Three" Four
endlocal
exit /b 0

:Test <Title> <Lines...> <LastArg>
echo %2
set "Temp=%4"
if defined Temp shift /2 & goto Test
echo %1
echo %3
exit /b 0

Output

"Two"
"Three"
One
Four

2. Put the string in a variable and pass the variable name

@echo off
setlocal EnableDelayedExpansion
set "String="Two","Three""
call :Test One String Four
endlocal
exit /b 0

:Test <a> <b> <c>
echo %1
echo !%2!
echo %3
exit /b 0

Output

One
"Two","Three"
Four

These are the first two solutions that come to my mind.

Update

Here is the shift method applied to your code using an inner loop :__Test

@echo off
setlocal EnableDelayedExpansion
call :Test Title "This is line 1","This is line 2" "arg3"
endlocal
exit /b 0

:Test <arg1> <arg2[,...]> <arg3>
set "i=0"
:__Test
set /a "i+=1"
set "val[!i!]=%2"
set "tempvar=%4"
if defined tempvar shift /2 & goto __Test
set "out="
for /l %%n in (1,1,!i!) do (
  set out=!out! !val[%%n]! ^& vbcrlf ^& _)
echo %1 !out:~1,-12! %3
exit /b 0

Upvotes: 3

Related Questions