29

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
0

4 Answers 4

54

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=,".

Sign up to request clarification or add additional context in comments.

5 Comments

Does it also handle when one of the theme name has whitespace? (I'm stuck on this issue now..)
I don't think you even need the string replacement for commas. see: stackoverflow.com/a/8086103/82211
@Justin: Yes, but in this case the solution does not "also handle when one of the theme name has whitespace".
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
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)
29

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
)

Comments

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
    

5 Comments

Thanks for the tip! Could you explain the line: set list=%~1 What does %~1 means?
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 :)
Is it for a certain reason that :getLineNumber is called with %%f and :parse with "%%g" (in quotes)?
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.
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.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.