anandr
Hi all
I have a string made of many parts separated by ";".
I need to loop through all the parts.
I tried FOR loop, but it returns either whole string (solution #1) or only first part (solution #2).
Solution #3 works better, but total number of parts is unknown.
I came to the solution #4. It works the way I need.
Is it possible to have the same result with just FOR loop without GOTO?
Code: Select all
@Echo off
set text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
echo.
set text
echo.
Echo -=[ Solution #1 ]=-
for /f "tokens=* delims=;" %%i in ("%text%") do (
Echo %%i
Echo %%k
)
echo.
Echo -=[ Solution #2 ]=-
for /f "delims=;" %%i in ("%text%") do (
Echo %%i
Echo %%k
)
echo.
Echo -=[ Solution #3 ]=-
for /f "tokens=1,2,* delims=;" %%i in ("%text%") do (
Echo %%i
Echo %%j
Echo %%k
Echo %%l
)
echo.
echo -=[ Solution #4 ]=-
set TN=1
:LL01
set part1=
for /f "tokens=%TN% delims=;" %%i in ("%text%") do (
set part1=%%i
)
:: double quotes are important here, because string parts may contain spaces!
if "%part1%" neq "" (
Echo %part1%
SET /A TN+=1
goto LL01
)
Everything will be OK at the end. If it is not OK -- it is not the end.text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
-=[ Solution #1 ]=-
p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
%k
-=[ Solution #2 ]=-
p1
%k
-=[ Solution #3 ]=-
p1
p2
p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
%l
-=[ Solution #4 ]=-
p1
p2
p3
p4
p5
p6
p7
p8
p9
p10
p11
p12
----------------------------
#2 01 Apr 2010 20:38
RG
How about this?
Code: Select all
@Echo off
set text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
echo.
set text
echo.
Echo -=[ Solution #1 ]=-
call :parse "%text%"
pause
goto :eof
:Parse
for /f "tokens=1* delims=;" %%i in ("%~1") do (
echo %%i
call :Parse "%%j"
)
goto :eof
Windows Shell Scripting and InstallShield
----------------------------
#3 02 Apr 2010 11:12
anandr
Thanks, RG. Your solution is definitely more elegant and easy to understand.
By the way, my solution #4 never returns more than 30 parts.
Is there a limit for number of tokens in FOR command?
After I played a little with the RG's code
I become curious what are the limitations for this method.
Here is two most important things I found:
1) Recursion level is limited by stack size
2) Command prompt (Cmd. exe) command-line string limitation
On computers running Microsoft Windows XP or later,
the maximum length of the string that you can use at the command prompt is 8191 characters.
On computers running Microsoft Windows 2000 or Windows NT 4.0,
the maximum length of the string that you can use at the command prompt is 2047 characters.
http://support.microsoft.com/kb/830473
Does anybody have an idea how to overcome the first limit?
The two scenarios can be illustrated with this script (it is basically RG's code with some make-up):
tester.cmd
Code: Select all
@Echo off
setlocal enabledelayedexpansion
if [%1] EQU [] goto PrintUsage
:: I use second argument to make %text% variable longer
set PartPrefix=%~2
echo Testing string composed of %1 parts. Prefix="%PartPrefix%"
:: Initializing %text% variable
FOR /L %%i IN (0,1,%1) DO set text=!Text!;%PartPrefix%%%i
call :parse "%text%"
echo Testing done
goto TheVeryEnd
:Parse
for /f "tokens=1* delims=;" %%i in ("%~1") do (
rem echo %%i
call :Parse "%%j"
)
goto TheVeryEnd
:PrintUsage
echo.
echo Usage: %~n0 ^<NumberOfParts^> [^<PartPrefix^>]
goto TheVeryEnd
:TheVeryEnd
endlocal
Last edited by anandr (02 Apr 2010 11:17)C:\ProgramFiles\batch\recursion_test>Tester.cmd 500
Testing string composed of 500 parts. Prefix=""
Testing done
C:\ProgramFiles\batch\recursion_test>Tester.cmd 800
Testing string composed of 800 parts. Prefix=""
****** B A T C H R E C U R S I O N exceeds STACK limits ******
Recursion Count=753, Stack Usage=90 percent
****** B A T C H PROCESSING IS A B O R T E D ******
C:\ProgramFiles\batch\recursion_test>Tester.cmd 500 EachPartIsNowLonger
Testing string composed of 500 parts. Prefix="EachPartIsNowLonger"
The input line is too long.
:Parse
was unexpected at this time.
C:\ProgramFiles\batch\recursion_test>
Everything will be OK at the end. If it is not OK -- it is not the end.
----------------------------
#4 02 Apr 2010 17:46
RG
For practical purposes the limits for tokens in FOR loops are a-z and A-Z (case sensitive).
Check out the following link if you need more than that.
http://www.robvanderwoude.com/clevertri ... stExtended
Don't know about increasing the stack size. I will look later.
Windows Shell Scripting and InstallShield
----------------------------
#5 02 Apr 2010 22:51
RG
Well one needs to be careful with GOTO, but you might consider something like this:
Code: Select all
Echo off
set text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12
REM Replace ; with space and don't use quotes because we want multiple arguments instead of 1
set text=%text:;= %
call :parse %text%
pause
goto :eof
:Parse
echo.%1
shift
if not "%1"=="" GOTO :Parse
goto :eof
Perhaps instead of putting all of your input into a variable, you should write it to a temp file. As you put it in there, break it up so that each "part" is a single line in your temp file and then just iterate through the temp file. Do something like this at the beginning of your program to specify a text file with the same name as you bat file in the temp folder and delete an existing leftover file (in case you aborted program).
Code: Select all
set TempFile=%TEMP%\%~n0.txt
if exist %TempFile% del /q %TempFile%
Code: Select all
if exist %TempFile% del /q %TempFile%
Windows Shell Scripting and InstallShield
----------------------------
#6 15 Apr 2010 15:52
SmartGenius
I Think that the Semicolon has treated by the CMD as a Space, just call a Function in your code and return the parameter you want, like in this example to get a Random Path Dir
Code: Select all
@Echo off
:Int
Set /a "Rnd=(%Random% %% 4)"
Call :GetDir %Path%
Echo. %Target%
Pause
Exit
:GetDir
For /l %%a in (1,1,%Rnd%) do (Shift)
Set "Target=%1"
Goto :Eof
----------------------------
#7 16 Apr 2010 23:31
carlos
Check this solution
Code: Select all
@echo off
setlocal enableextensions
set "text=p1;p2;p3;p4;p5;p6;p7;p8;p9;p10;p11;p12"
call :iterate text
pause
goto :eof
:iterate:
setlocal enableextensions enabledelayedexpansion
:iterate_:
for /f "tokens=1,* delims=;" %%a in (
"!%1!") do set "%1=%%b" & echo.%%a
if "!%1!" neq "" goto :iterate_:
goto :eof