Batch/cmd - subroutine does not "return" array via parameter

batch file call subroutine
batch file call command with parameters
batch file function with parameters
batch file subroutine parameters
batch file goto not working
batch file with parameters example
go to in bat file
batch file break

In the following script I want to pass a string via variable and the variable name for an array which should contain substrings to a subroutine.

The subroutine puts substrings of the passed string into an array/list which then should get "returned" by setting it as the value of the 2. passed parameter.

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
    SET testString=Hello World

    REM Pass testString and substrings to subroutine
    CALL :get_substrings testString substrings

    REM For testing. Echo substrings. DOESN'T WORK. substrings is empty!
    FOR /L %%s IN (0,1,2) DO (
        ECHO !substrings[%%s]!
    )
ENDLOCAL

EXIT /B 0

:get_substrings
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET "string=!%~1!"

    REM Alternative approach: Make a connection to %2 rightaway
    REM SET "substrings=!%~2!"

    REM Process string: Put substrings into indexed array. This works as expected!
    FOR /L %%s IN (0,1,2) DO (
        SET substrings[%%s]=!string:~0,5!
        SET string=!string:~5!
    )

    REM For testing. Echo the substrings. Works as expected!
    FOR /L %%s IN (0,1,2) DO (
        ECHO !substrings[%%s]! 
    )

    REM For alternative approach
    REM ENDLOCAL

    REM End the local the set 2.param = substringsArray
    ENDLOCAL & SET %2=%substrings%
EXIT /B 0

Processing the string by creating a array with substrings in the subroutine works as expected. But setting 2. parameters value and keeping the value after subroutine doesn't work...

Notes: The processing of the string is just a dummy. The real process is slightly different but the core with the substrings array is the same. The script is executable right away.

So, how can I get the value substrings back?

This does what you want:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
    SET testString=Hello World

    REM Pass testString and substrings to subroutine
    CALL :get_substrings testString substrings

    REM For testing. Echo substrings.
    FOR /L %%s IN (0,1,2) DO (
        ECHO !substrings[%%s]!
    )
ENDLOCAL

EXIT /B 0

:get_substrings
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET "string=!%~1!"

    REM Process string: Put substrings into indexed array. This works as expected!
    FOR /L %%s IN (0,1,2) DO (
        SET substrings[%%s]=!string:~0,5!
        SET string=!string:~5!
    )

    REM For testing. Echo the substrings. Works as expected!
    FOR /L %%s IN (0,1,2) DO (
        ECHO !substrings[%%s]! 
    )

    REM End the local the set 2.param = substringsArray
    set SubEnviron=1
    for /F "tokens=2* delims=[]=" %%a in ('set substrings[') do (
        if defined SubEnviron ENDLOCAL
        set "%2[%%a]=%%b"
    )

EXIT /B 0

Using Batch File Subroutines, In other scripting languages such as VBScript, the end of the "main program" is unmistakable, but in the batch file language it is not. You must use  Batch files are not structured programs; they are a sequence of instructions with some BASIC-like facility for GOTO and CALL. If you want to return from a CALL, you use EXIT command with /B argument (as "EXIT" alone will terminate the batch file).

I wasn't able to understand your counting of characters so here's how I'd probably do it:

@Echo Off
SetLocal EnableDelayedExpansion
Set "TestString=Hello World"

For /F "Delims==" %%A In ('Set SubString[ 2^>Nul') Do Set "%%A="
Set "i=1"
Set "SubString[%i%]=%TestString: ="&Set/A i+=1&Set "SubString[!i!]=%"

Set SubString[
Pause

Example Output:

SubString[1]=Hello
SubString[2]=World
Press any key to continue . . .

For the purposes of testing you probably don't need the For loop, its purpose is to ensure there are no existing variables whose name begins with SubString[

Edit

This uses three parameters:

  1. The string to cut%string%
  2. A number of how long each substring should be%chrnum%
  3. The substring parameter%strvar%

@Echo Off
SetLocal EnableDelayedExpansion

Set "string=montuewedthufrisatsun"
Set "chrnum=3"
Set "strvar=substring"

Set "i=1"
Set "_=%string%"

:Loop
Set "!strvar![%i%]=!_:~,%chrnum%!"
If "!_:~%chrnum%!"=="" GoTo Write
Set "_=!_:~%chrnum%!"
Set /A i+=1
GoTo Loop

:Write
Set !strvar![ 2>Nul
Pause

Call - Windows CMD, Call one batch program from another, or call a subroutine. the parent batch program" it is true that the parent does not STOP, but it does PAUSE The CALL command will launch a new batch file context along with any specified parameters. does not return. In other words, the default mode for batch file invocation is chain.) In other words, the call command lets you invoke another batch file as a subroutine. The command line parameters are received by the other batch file as the usual numbered parameters %1, %2, etc. It's annoying having to put every subroutine inside its own

Yes, I fully understand that you are not going to change this code to PowerShell. But, it might be worth considering for the next time given how easy it is. get_substrings is a lambda.

PS C:\src\t\selarr> type .\lamb002.ps1
$teststring = 'hello cruel world'

$get_substrings = { param($t) foreach ($s in $t.split()) { $s.Substring(0,4) } }

$a = & $get_substrings $teststring
$a.length
$a[0]
$a[1]
$a[2]

PS C:\src\t\selarr> .\lamb002.ps1
3
hell
crue
worl

GOSUB, If the label still is not found, the batch file is terminated with the error message "​Label not found". You can define GOSUB variables by placing them after the label  GOSUB can only be used in batch files. TCC allows subroutines in batch files. A subroutine must start with a label (a colon [:] followed by a label name) which appears on a line by itself, and cannot be included a command group. Case differences are ignored when matching labels. The subroutine must end with a RETURN statement.

Exit batch file from subroutine, @ECHO OFF SETLOCAL IF "%selfWrapped%"=="" ( REM this is necessary so that we can use "exit" to terminate the batch file, REM and all subroutines, but not​  The called batch file should always either return (by executing its last line, or by using the QUIT command), or it should terminate batch file processing with CANCEL. Do not restart or CALL the original batch file from within the called file as this may cause an infinite loop or a stack overflow.

Exiting a batch file without exiting the command shell -and- batch file , Prepare your party hats: Batch File Week is almost over. when you start playing with batch file subroutines. Okay, let's back does not return. Thus, when you call `exit /b`, that command sets the ERRORLEVEL and then the batch script exits with a code of 0 (because it successfully executed). Thus, the test command above may indeed set an ERRORLEVEL, but it will not return a failure exit code, such that the command `echo fail` will actually execute.

Batch files, In "real DOS", the GOTO command is used to skip part of a batch file: There is, of course, always a way to fake subroutines in batch files: Always terminate a label with a line feed, or ("true") DOS won't be able to find it. Batch Script - Functions. A function is a set of statements organized together to perform a specific task. In batch scripts, a similar approach is adopted to group logical statements together to form a function. Function Declaration − It tells the compiler about a function's name, return type, and parameters.

Comments
  • The simplest way to do what you want (that is, that the array variables created in the subroutine could be accessed in the caller program) is eliminate SETLOCAL / ENDLOCAL from the subroutine and use %~2 instead substrings varname. So the real question here is: why you need to use SETLOCAL in the subroutine? You can explicitly delete the not desired variables at subroutine end...
  • So the real question here is Yeah, you are right... Maybe its not really necessary. It worked here thats why I wanted to know why it isn't working in my case.
  • Yes, it works in your example because it returns just one variable and an array in Batch is comprised of several variables... See my solution. I also suggest you to read this post about "arrays" in Batch.
  • I have one question about the code. Why is it necessary that the delimiter is "... delims=[]=" with an extra equal sign and not "... delims=[]" ?
  • The set substrings[ command list values like this one: substrings[1]=Hello. If you not include the equal sign in the delimiters then the assigned value would include it; that is, the set "%2[%%a]=%%b" line would become set "substrings[1]==Hello".
  • I wasn't able to understand your counting of characters. That isn't important, just how I said: The processing of the string is just a dummy. The real process is slightly different but the core with the substrings array is the same.
  • @goulashsoup , if you replace Hello World with your real string of substrings, what does the above code output? and is that what you intended it to do?
  • The get_substrings routine actually expects 3 parameters. 1. The string to cut. 2. A number of how long each substring should be 3. The substring parameter which should later be used in the main part. The goal of the subroutine is to cut a long message into substrings so they can be outputed in more then one line. Because this process allreadys works and is not part of the question I simplified the get_substrings routine with a dummy process which also creates a substrings array just like my process. The goal of the question is the answer of how I get back the substrings array.
  • With the above comment it should be clear that substring doesn't mean word by word.
  • @goulashsoup, I'd suggest you rewrite your question and code to make them both accurate and pertinent then. For instance if your question was I have the following, @Echo Off, Set string=montuewedthufrisatsun", Set chrnum=3. I'd like to split %string% into an array of variables, in the form %substring[#]%, each value having the number of characters, determined by the %chrnum% value. Example %substring[1]%=mon, %substring[2]%=tue, %substring[3]%=wed, %substring[4]%=thu, %substring[5]%=fri, %substring[6]%=sat and %substring[7]%=sun. Would that not better explain things?