1

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?

goulashsoup
  • 2,639
  • 2
  • 34
  • 60
  • 2
    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... – Aacini May 21 '18 at 01:39
  • _So the real question here is_ Yeah, you are right... Maybe its not really necessary. It worked [here](https://www.tutorialspoint.com/batch_script/batch_script_string_length.htm) thats why I wanted to know why it isn't working in my case. – goulashsoup May 21 '18 at 01:48
  • 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](https://stackoverflow.com/a/50441021/778560). I also suggest you to read [this post](https://stackoverflow.com/questions/10166386/arrays-linked-lists-and-other-data-structures-in-cmd-exe-batch-script/10167990#10167990) about "arrays" in Batch. – Aacini May 21 '18 at 01:55

3 Answers3

2

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
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • I have one question about the code. Why is it necessary that the delimiter is `"... delims=[]=" ` with an extra equal sign and not `"... delims=[]" ` ? – goulashsoup May 21 '18 at 13:55
  • 1
    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"`. – Aacini May 21 '18 at 15:43
1

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
Compo
  • 36,585
  • 5
  • 27
  • 39
  • _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 May 21 '18 at 01:18
  • @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? – Compo May 21 '18 at 01:21
  • 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. – goulashsoup May 21 '18 at 01:31
  • With the above comment it should be clear that substring doesn't mean word by word. – goulashsoup May 21 '18 at 01:34
  • 1
    @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? – Compo May 21 '18 at 01:54
  • @goulashsoup, I have added an edit to my original answer, to show a method of what I wrote in the above comment. Additionally, in your comment above you seem to have stated specifically what your question is, 'The goal of the question is the answer of how I get back the substrings array.' I have shown that in both the initial response and my edit, where I used the `Set` command, `Set SubString[` and `Set !strvar![ 2>Nul`. – Compo May 21 '18 at 04:50
  • First I have to thank you for your effort. _Would that not better explain things?_ Like I stated in the question I allready have a working routine for that. So why should I ask how I should do it if its allready working? The only problem I had was to get the array back from one local to another. The only one who seems to understand that was [Aacini](https://stackoverflow.com/users/778560/aacini). But I think I can use some of your code too, so thank you... – goulashsoup May 21 '18 at 12:28
  • @goulashsoup, Aacini showed you how to use the `Echo` command to show the variables from a `For /L` loop. What would happen if you just replaced the two `FOR /L %%s IN (0,1,2) DO (ECHO !substrings[%%s]!` to `SET substrings[`; or if you wanted to return just the values, `For /F "Tokens=1* Delims==" %%A In ('Set Substrings[ 2^>Nul') Do Echo %%B`. Not only should these work, but they would also prevent the need to adjust the code every time the number of possible iterations of `substrings[#]` occurred! _Even LotPings answer told you that your code was limiting the number of substrings!_ – Compo May 21 '18 at 12:45
1

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
lit
  • 14,456
  • 10
  • 65
  • 119