28

Windows

Based on the post (dos batch iterate through a delimited string), I wrote a script below but not working as expected.

Goal: Given string "Sun,Granite,Twilight", I want to get each theme value in loop so that I can do some processing with the value.

Current output is not corrct:

list = "Sun,Granite,Twilight"
file name is "Sun Granite Twilight"

For the first iteration it should be:

list = "Sun,Granite,Twilight"
file name is "Sun"

Then second iteration should be "file name is "Granite" and so on. What am I doing wrong?

Code:

set themes=Sun,Granite,Twilight

call :parse "%themes%"
goto :end

:parse
setlocal
set list=%1
echo list = %list%
for /F "delims=," %%f in ("%list%") do (
    rem if the item exist
    if not "%%f" == "" call :getLineNumber %%f
    rem if next item exist
    if not "%%g" == "" call :parse "%%g"
)
endlocal

:getLineNumber
setlocal
echo file name is %1
set filename=%1
endlocal

:end
Community
  • 1
  • 1
Meow
  • 18,371
  • 52
  • 136
  • 180

4 Answers4

51

This is the way I would do that:

@echo off
set themes=Sun,Granite,Twilight
echo list = "%themes%"
for %%a in ("%themes:,=" "%") do (
   echo file name is %%a
)

That is, change Sun,Granite,Twilight by "Sun" "Granite" "Twilight" and then process each part enclosed in quotes in a regular (NO /F option) for command. This method is much simpler than an iterative for /F loop based on "delims=,".

Aacini
  • 65,180
  • 12
  • 72
  • 108
  • 1
    Does it also handle when one of the theme name has whitespace? (I'm stuck on this issue now..) – Meow Jun 18 '13 at 02:18
  • 1
    I don't think you even need the string replacement for commas. see: http://stackoverflow.com/a/8086103/82211 – Justin Dec 18 '13 at 06:24
  • @Justin: Yes, but in this case the solution does not "also handle when one of the theme name has whitespace". – Aacini Dec 18 '13 at 06:41
  • 4
    This is really neat! Took me a bit to understand, but basically the "for" command will by default 1) use space as the delimiter, and 2) understand double-quoted items to be single items. That means `for %%a in ("one" "two" "three and four") do echo %%a` will give the output `one\ntwo\three and four` – rocketmonkeys May 09 '14 at 17:18
  • its really ***ty that microsoft doesn't bother to document relevant information where it is needed and always tucks it away in some web page or obscure location that you are expected to "just know" how/where to find it inside their obscene mountain of pasta that is (ahem, *was*) MSDN (they can't even keep product their names the consistent, let alone documentation) I wager most M$ meetings consist of mainly name-changing and asset-moving (instead of improving it, they just move it somewhere new) – osirisgothra Jul 23 '23 at 16:26
28

I took Aacini's answer and only slightly modified it to remove the quotes, so that the quotes can be added or removed as it would be in the desired command.

@echo off
set themes=Hot Sun,Hard Granite,Shimmering Bright Twilight
for %%a in ("%themes:,=" "%") do (
    echo %%~a
)
Community
  • 1
  • 1
Brent
  • 381
  • 3
  • 4
12

I made a few modifications to your code.

  1. Need goto :eof at end of subroutines and at end of main routine so you don't fall into subroutines.
  2. tokens=1* (%%f is first token; %%g is rest of the line)
  3. ~ in set list=%~1 to remove quotes so quotes don't accumulate

    @echo off
    set themes=Sun,Granite,Twilight
    
    call :parse "%themes%"
    pause
    goto :eof
    
    :parse
    setlocal
    set list=%~1
    echo list = %list%
    for /F "tokens=1* delims=," %%f in ("%list%") do (
        rem if the item exist
        if not "%%f" == "" call :getLineNumber %%f
        rem if next item exist
        if not "%%g" == "" call :parse "%%g"
    )
    endlocal
    goto :eof
    
    :getLineNumber
    setlocal
    echo file name is %1
    set filename=%1
    goto :eof
    
RGuggisberg
  • 4,630
  • 2
  • 18
  • 27
  • Thanks for the tip! Could you explain the line: set list=%~1 What does %~1 means? – Meow Jun 18 '13 at 00:18
  • Just for my understanding: is :eof a "builtin" label? Since I don't see it defined. And in the subroutine it acts as "return"? Strange control flow, though :) – Krischu Mar 17 '17 at 10:03
  • Is it for a certain reason that :getLineNumber is called with %%f and :parse with "%%g" (in quotes)? – Krischu Mar 17 '17 at 11:13
  • No reason other than that is the way it was in the original post so I did not change it. If I was doing it from scratch I would quote the %%f as well just to avoid trouble in the future if a string contains spaces, etc. – RGuggisberg Mar 17 '17 at 13:08
  • Just to improve your code: replace `goto :eof` with `exit /b` at the call's return point. It is cleaner, avoids writing the `:eof` label on each batch (if you have many of them) and also allows to return with errors by placing `exit / b ErrorValue` (`ErrorValue` must be an integer). In this last way after the call you can handle the errors as if they were exceptions, where you can capture them and make the main program decide what to do about it. – Noir Jul 09 '19 at 16:10
0

Looks like it needed the "tokens" keyword...

@echo off
set themes=Sun,Granite,Twilight

call :parse "%themes%"
goto :end

:parse
setlocal
set list=%1

for /F "delims=, tokens=1*" %%f in (%list%) do (
    rem if the item exist
    if not "%%f" == "" call :getLineNumber %%f
    rem if next item exist
    if not "%%g" == "" call :parse "%%g"
)
endlocal
goto :end

:getLineNumber
setlocal
echo file name is %1
set filename=%1
endlocal 

:end
dazedandconfused
  • 3,131
  • 1
  • 18
  • 29