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:
它获取%中变量的值,并将每个逗号分隔的行读入数组的一个元素,然后对其执行一些字符串连接并输出一个新的字符串。现在,当我试图将它转换为函数时,它失败了,因为将%2解析为一个参数。我需要将%2解析为一个带变量值的、用逗号分隔的字符串。这个简单的测试不起作用:
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.
我能想到的唯一的事情是使用%*并将分隔符更改为一些惟一的东西,但是如果可能的话,我宁愿避免这样做。
2 个解决方案
#1
3
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
.
这将使%1保持不变,并在没有其他参数时转移所有中间参数,并在%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
下面是使用内部循环应用到代码的转换方法:__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
#2
2
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.
还有另一种方法,但是它需要调用者做更多的工作。您可以要求参数值中的所有引号都加倍,然后将整个参数包含在一组引号中。在函数中,将“all”替换为“with”以获得所需的值。我以前使用这种方法,直到我学会了通过引用传递值。
@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 ?
.
但是OP并不真正对作为单个参数的值列表感兴趣,因为FOR循环用于分割它们。如果任何值包含*或?,则FOR循环可能会遇到麻烦。
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.
我现在看到,对于这个特定的情况,最好将列表移到末尾,这样3上的所有参数都是列表的一部分。然后使用SHIFT /3和GOTO循环来读取列表中的值。这基本上是大卫·鲁曼的选项1。
#1
3
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
.
这将使%1保持不变,并在没有其他参数时转移所有中间参数,并在%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
下面是使用内部循环应用到代码的转换方法:__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
#2
2
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.
还有另一种方法,但是它需要调用者做更多的工作。您可以要求参数值中的所有引号都加倍,然后将整个参数包含在一组引号中。在函数中,将“all”替换为“with”以获得所需的值。我以前使用这种方法,直到我学会了通过引用传递值。
@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 ?
.
但是OP并不真正对作为单个参数的值列表感兴趣,因为FOR循环用于分割它们。如果任何值包含*或?,则FOR循环可能会遇到麻烦。
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.
我现在看到,对于这个特定的情况,最好将列表移到末尾,这样3上的所有参数都是列表的一部分。然后使用SHIFT /3和GOTO循环来读取列表中的值。这基本上是大卫·鲁曼的选项1。