18

Say I have a string such as foo:bar:baz, is it possible to loop through this string? It looked like you could tokenize lines of a file but the following will only echo 'foo' once.

for /f "tokens=1,2* delims=:" %%x in ("%%j") do echo %%x

Tomalak
  • 332,285
  • 67
  • 532
  • 628
Joe Cartano
  • 2,997
  • 3
  • 22
  • 40

4 Answers4

21
set string=foo:bar:baz
for %%x in (%string::= %) do echo %%x

FOR value delimiters may be space, comma, semicolon and equal-sign. You may directly process a string if the elements are delimited with any of these characters. If not, just change the delimiter for one of these character (as I did above).

set string=foo bar,baz;one=two
for %%x in (%string%) do echo %%x
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • Could you explain the delimeter syntax above a little bit more please? – Wonko the Sane Aug 09 '23 at 18:22
  • 1
    @WonkotheSane: I am afraid I don't understand what you refer by "delimiter syntax". The `%string::= %` is just a _string replacement_ that changes semicolons by spaces. The FOR value delimiters are comma, semicolon and equal-sign, besides space and TAB, as described [here](https://ss64.com/nt/syntax-esc.html#delimiters) – Aacini Aug 10 '23 at 19:19
  • Do you mean changes _colons_ by spaces? That's where I'm getting lost. – Wonko the Sane Aug 11 '23 at 17:39
  • 1
    @WonkotheSane: You can read about _string replacement_ at [this page](https://ss64.com/nt/syntax-replace.html) – Aacini Aug 11 '23 at 19:25
  • From that page- `%variable:StrToFind=NewStr%` So, if I were to read your second line, I'm reading it as "%string:" is "look at the variable string", then for the StrToFind you have ":" which I am thinking means "look for the value colon (:)", and "= %" means "and replace it with a space." – Wonko the Sane Aug 14 '23 at 19:34
11

Aacini beat me to my first solution. It can be improved by adding quotes so that the string can contain spaces and special characters and still give the correct result.

set "str=foo bar:biz bang:this & that"
for %%S in ("%str::=" "%") do echo %%~S

The solution has limitations:

  • No * or ? can appear in the string
  • Problems can arise if the string already contains quotes ("), especially if there are special characters as well

The second solution has bizarre syntax, but the concept is fairly straight forward. FOR /F with "string" will break on linefeed characters - each line will be processesed as its own string. The trick is to replace the : delimiter with a linefeed character. Note that the blank line in the solution below is a critical part of the tricky replacement. Also, the following line must start with the ! which terminates the delayed variable expansion. There should not be any leading spaces.

The other thing to worry about is the pesky FOR /F "EOL" option. We don't want to skip any values that start with the EOL character which is ; by default. Since we have eliminated the string delimiter :, we can safely use that as the EOL.

Finally, we need to use delayed expansion, but ! will be corrupted during %%S expansion if we don't first disable delayed expansion within the loop.

set "str=foo bar:biz bang:this & that "^& the other thing!":;How does this work?"
setlocal enableDelayedExpansion
set ^"str=!str::=^

!"
for /f "eol=: delims=" %%S in ("!str!") do (
  if "!!"=="" endlocal
  echo %%S
)

I believe this technique can handle just about anything you throw at it.

jeb was the first to show me how to work with line feeds within batch, and especially how to use this technique. It may even be posted already somewhere else on SO.

dbenham
  • 127,446
  • 28
  • 251
  • 390
  • Can you tell me why are you escaping the first quotation mark like `^"` at the string replacement? and why do you not escape the second one? – aschipfl May 20 '16 at 13:34
  • Ah damn, stupid me! I got it know: an unescaped `"` would make the `^` to be kept literally... strangely I found that out not by testing the code but by attending to something completely different since I posted the comment; quite nerdy, is it not? ;-) – aschipfl May 20 '16 at 18:13
3

it does as expected, it tokenizes it to %%x %%y %%z. So instead of just processing %%x, you might...

for /f "tokens=* delims=:" %%x in ("foo:bar:baz") do (
  echo %%x
  echo %%y
  echo %%z
)

or if you don't know in advance how many items you got, you may...

@echo off
set string=foo:bar:baz
:again
for /f "tokens=1* delims=:" %%x in ("%string%") do (
   echo %%x 
   set string=%%y
)
if not .%string%==. goto again
echo done
PA.
  • 28,486
  • 9
  • 71
  • 95
  • hmm, I guess what I want is to be able to loop through tokens like I can loop through lines in a file. A nice way of doing this: http://stackoverflow.com/questions/6966558/batch-file-for-f-tokens – Joe Cartano Dec 13 '11 at 18:06
3

This will parse the string into variables accessable outside of the loop (;

setlocal enabledelayedexpansion

set your_string="whatever:you:want"

for /f "tokens=1,2,3 delims=:" %%a in ('echo !your_string!') do (
    set first_sub_string=%%a
    set second_sub_string=%%b
    set third_sub_string=%%c
)

echo Congrats.  !your_string! was parsed as !first_sub_string!, !second_sub_string!, !third_sub_string!
kikuchiyo
  • 3,391
  • 3
  • 23
  • 29