很有趣的問題,我很驚訝它是多麼容易解決:-)
編輯- 我原來的答覆並沒有完全回答這個問題,正如Aacini在他的評論中指出的那樣。最後,我有一個直接回答問題的版本。我還更新了我的原始答案,以包含我發現的更多限制
如果您規定要返回的所有變量的名稱都帶有固定前綴,您可以非常容易地返回任意數量的變量。返回變量前綴可以作爲參數傳入。
下面的行是所有需要:任何迭代發生之前
for /f "delims=" %%A in ('set prefix.') do endlocal & set "%%A"
的set prefix
命令的全部結果是緩衝的。第一次迭代執行返回到CALL之前存在的環境狀態所需的唯一ENDLOCAL。後續的ENDLOCAL迭代不會造成危害,因爲CALLed函數中的ENDLOCAL只對在CALL中發出的SETLOCAL有效。額外的冗餘ENDLOCAL被忽略。
有這個非常簡單的解決方案的一些非常不錯的功能:
- 有理論上沒有限制返回的變量的數目。
- 返回的值可以包含幾乎任何字符的組合。
- 返回的值可以接近8191字節長的理論最大長度。
也有一些限制:
- 返回的值不能包含換行符
- 如果返回值的最後一個字符是一個回車,即最終的回車將被剝離。
- 如果在進行CALL時啓用延遲擴展,則任何包含
!
的返回值都將被損壞。
- 我還沒有想出一個優雅的方法來設置一個返回的變量爲undefined。
這裏是一個返回可變數值
@echo off
setlocal
set varBeforeCall=ok
echo(
call :variadic callA 10 -34 26
set callA
set varBeforeCall
echo(
call :variadic callB 1 2 5 10 50 100
set callB
set varBeforeCall
exit /b
:variadic returnPrefix arg1 [arg2 ...]
@echo off
setlocal enableDelayedExpansion
set /a const=!random!%%100
:: Clear any existing returnPrefix variables
for /f "delims==" %%A in ('set %1. 2^>nul') do set "%%A="
:: Define the variables to be returned
set "%~1.cnt=0"
:argLoop
if "%~2" neq "" (
set /a "%~1.cnt+=1"
set /a "%~1.!%~1.cnt!=%2*const"
shift /2
goto :argLoop
)
:: Return the variables accross the ENDLOCAL barrier
for /f "delims=" %%A in ('set %1. 2^>nul') do endlocal & set "%%A"
exit /b
的這裏一個可變參數函數的一個簡單的例子是一個示例運行的結果:
callA.1=40
callA.2=-136
callA.3=104
callA.cnt=3
varBeforeCall=ok
callB.1=24
callB.2=48
callB.3=120
callB.4=240
callB.5=1200
callB.6=2400
callB.cnt=6
varBeforeCall=ok
這裏是一個版本在啓用延遲擴展時可以安全調用
有點分機ra代碼,可以在啓用延遲擴展並且返回值包含!
的情況下刪除有關調用該功能的限制。
當啓用延遲擴展時,必要時會處理返回的值以保護!
。該代碼已經過優化,因此只有在啓用了延遲擴展並且該值包含!
時才執行相對昂貴的小型處理(特別是CALL)。
返回的值仍然不能包含換行符。一個新的限制是,如果返回的值包含!
,並且在進行CALL時啓用了延遲擴展,則所有回車將被剝離。
這是演示。
@echo off
setlocal
set varBeforeCall=ok
echo(
echo Results when delayed expansion is Disabled
call :variadic callA 10 -34 26
set callA
set varBeforeCall
setlocal enableDelayedExpansion
echo(
echo Results when delayed expansion is Enabled
call :variadic callB 1 2 5 10 50 100
set callB
set varBeforeCall
exit /b
:variadic returnPrefix arg1 [arg2 ...]
@echo off
:: Determine if caller has delayed expansion enabled
setlocal
set "NotDelayed=!"
setlocal enableDelayedExpansion
set /a const=!random!%%100
:: Clear any existing returnPrefix variables
for /f "delims==" %%A in ('set %1. 2^>nul') do set "%%A="
:: Define the variables to be returned
set "%~1.cnt=0"
:argLoop
if "%~2" neq "" (
set /a "%~1.cnt+=1"
set /a "%~1.!%~1.cnt!=%2*const"
shift /2
goto :argLoop
)
set %~1.trouble1="!const!\^^&^!%%"\^^^^^&^^!%%
set %~1.trouble2="!const!\^^&%%"\^^^^^&%%
:: Prepare variables for return when caller has delayed expansion enabled
if not defined NotDelayed for /f "delims==" %%A in ('set %1. 2^>nul') do (
for /f delims^=^ eol^= %%V in ("!%%A!") do if "%%V" neq "!%%A!" (
set "%%A=!%%A:\=\s!"
set "%%A=!%%A:%%=\p!"
set "%%A=!%%A:"=\q!"
set "%%A=!%%A:^=\c!"
call set "%%A=%%%%A:^!=^^^!%%" ^^!
set "%%A=!%%A:^^=^!"
set "%%A=!%%A:\c=^^!"
set "%%A=!%%A:\q="!"
set "%%A=!%%A:\p=%%!"
set "%%A=!%%A:\s=\!"
)
)
:: Return the variables accross the ENDLOCAL barrier
for /f "delims=" %%A in ('set %1. 2^>nul') do endlocal & endlocal & set "%%A"
exit /b
和一些示例結果:
Results when delayed expansion is Disabled
Environment variable callA not defined
callA.1=780
callA.2=-2652
callA.3=2028
callA.cnt=3
callA.trouble1="78\^&!%"\^&!%
callA.trouble2="78\^&%"\^&%
varBeforeCall=ok
Results when delayed expansion is Enabled
Environment variable callB not defined
callB.1=48
callB.2=96
callB.3=240
callB.4=480
callB.5=2400
callB.6=4800
callB.cnt=6
callB.trouble1="48\^&!%"\^&!%
callB.trouble2="48\^&%"\^&%
varBeforeCall=ok
注意返回的麻煩值的格式是如何一致調用時延遲擴展是否被啓用。如果由於!
而導致延遲擴展不適用於額外的代碼,則trouble1值將被損壞。
編輯:這裏是直接回答這個問題
原來的問題規定,每個名字返回的變量都應該在參數列表中提供一個版本。我修改了我的算法,在函數內用一個點作爲每個變量名的前綴。然後,我稍微修改了最後返回的FOR語句以去掉前導點。有一個限制,即返回變量的名稱不能以點開頭。
此版本包含安全返回技術,允許在啓用延遲擴展時進行CALL。
@echo off
setlocal disableDelayedExpansion
echo(
set $A=before
set $varBeforeCall=ok
echo ($) Values before CALL:
set $
echo(
echo ($) Values after CALL when delayed expansion is Disabled:
call :variadic $A $B
set $
setlocal enableDelayedExpansion
echo(
set #A=before
set #varBeforeCall=ok
echo (#) Values before CALL:
set #
echo(
echo (#) Values after CALL when delayed expansion is Enabled:
call :variadiC#A #B #C
set #
exit /b
:variadic arg1 [arg2 ...]
@echo off
:: Determine if caller has delayed expansion enabled
setlocal
set "NotDelayed=!"
setlocal enableDelayedExpansion
:: Clear any existing . variables
for /f "delims==" %%A in ('set . 2^>nul') do set "%%A="
:: Define the variables to be returned
:argLoop
if "%~1" neq "" (
set /a num=!random!%%10
set ^".%~1="!num!\^^&^!%%"\^^^^^&^^!%%"
shift /1
goto :argLoop
)
:: Prepare variables for return when caller has delayed expansion enabled
if not defined NotDelayed for /f "delims==" %%A in ('set . 2^>nul') do (
for /f delims^=^ eol^= %%V in ("!%%A!") do if "%%V" neq "!%%A!" (
set "%%A=!%%A:\=\s!"
set "%%A=!%%A:%%=\p!"
set "%%A=!%%A:"=\q!"
set "%%A=!%%A:^=\c!"
call set "%%A=%%%%A:^!=^^^!%%" ^^!
set "%%A=!%%A:^^=^!"
set "%%A=!%%A:\c=^^!"
set "%%A=!%%A:\q="!"
set "%%A=!%%A:\p=%%!"
set "%%A=!%%A:\s=\!"
)
)
:: Return the variables accross the ENDLOCAL barrier
for /f "tokens=* delims=." %%A in ('set . 2^>nul') do endlocal & endlocal & set "%%A"
exit /b
和實例的結果:
($) Values before CALL:
$A=before
$varBeforeCall=ok
($) Values after CALL when delayed expansion is Disabled:
$A="5\^&!%"\^&!%
$B="5\^&!%"\^&!%
$varBeforeCall=ok
(#) Values before CALL:
#A=before
#varBeforeCall=ok
(#) Values after CALL when delayed expansion is Enabled:
#A="7\^&!%"\^&!%
#B="2\^&!%"\^&!%
#C="0\^&!%"\^&!%
#varBeforeCall=ok
尼斯Aacini,但也有一些限制:1)用引號和特殊字符值可能會導致問題。例子'「this&that」&other' 2)具有'*'和'?'的值將被破壞。 – dbenham
另一個限制 - 所有返回的定義的總和長度必須在〜8191字節內。 – dbenham
謝謝Dave,我借用了你的方法並改進了我的解決方案! ;) – Aacini