::::::::::::::::
@echo off
for /f "tokens=1*" %%a in ('echo "abc 123" xyz') do echo FOR split: [%%a]---[%%b]
call :shArg "abc 123" xyz
goto :eof
:shArg
echo CALL split: [%1]---[%2]
goto :eof
::::::::::::::::
on my box, the output looks like:
This shows the FOR command tokenizing quoted strings differently from how the command shell separates out command parameters. That can present a problem when your batch file wants to be able to recognize a variable number of parameters, some of which could contain spaces. In the specific case I ran into recently, an existing batch file, B1, served as an argument preprocessor for another batch program, B2. In general, B1 stepped through the given command line parameters, acting on each one that it recognized. When and if it comes to an unrecognized parameter, preprocessing is complete and all remaining unprocessed parameters are forwarded to B2. And there's the rub. How to get at the remaining parameters? If you can count on there being no quoted parameters (with embedded spaces) in the first or second position, and you want to pass all parameters after the second, you could probably get away withFOR split: ["abc]---[123" xyz] CALL split: ["abc 123"]---[xyz] c:\>
FOR /f "tokens=2*" %%a in ("%*") DO call B2 %%b
But that wouldn't give the desired result if, say, B1 had been invoked as
The quotes confuse the FOR parse.B1 -d "c:\my notes" readme.txt -A
We could do some shifts and then pass up to 10 arguments explicitly...
but that wouldn't work right if the command tail had more than 10 parameters.shift shift shift call B2 %0 %1 %2 %3 %4 %5 %6 %7 %8 %9
The best quick and dirty solution is probably to build up the desired command tail stepwise.
set cmdTail=%3 :top if %4.==. goto callB2 set cmdTail=%cmdTail% %4 shift goto top :callB2 call B2 %cmdTail%
If that's too easy, here's one that's sure to qualify as the Hard Way by any measure...
@echo off
:: file: detach.cmd
:: Example: detach "123 456" abc xyz
:: ["123 456"][abc xyz]
:: Example: detach 123 " 456 abc" xyz
:: [123][" 456 abc" xyz]
setlocal
set argList=%*
call :detachArg1 argList
echo [%detachArg1%][%argList%]
goto done
:: if no quotes, this is much simpler:
:: FOR /f "tokens=1*" %%a IN ("%*") DO echo [%%a][%%b]
:: detachArg1 arglistName
:: (split first arg from given argList)
:: detachArg1 = firstArg(*arglistName)
:: *arglistName = argTail(*arglistName)
:detachArg1
set _detachArg1=
if %1.==. goto :eof
call call :firstArg %%%1%%
call set %%^^%0%%=%firstArg%
call call :argTail %%%1%%
set %1=%argTail%
goto :eof
:firstArg
call set %%^^%0%%=%1
goto :eof
:: return argument list, without first.
:argTail
set argList=%*
set arg1=%1
if not defined arg1 goto :eof
call :varlength arg1
call call :trimlist %%argList:~%varlength%%%
call set %%^^%0%%=%trimlist%
goto :eof
:trimlist
call set %%^^%0%%=%*
goto :eof
:: get length of environment variable (via brute force)
:: %1 name of var to check
:varlength
for /f "tokens=1* delims==" %%y in ('set %1^|findstr /b /i "%1=" ') do (
set vl_val=%%z)
set vl_vlen=0
if not defined vl_val goto lenrpdone
:: trap quoted string
if "".==%vl_val:~0,1%%vl_val:~-1%. (
set vl_vlen=2
set vl_val=%vl_val:~1,-1%
) else (
set vl_vlen=0
)
:chklenrpdone
if "%vl_val%"=="" goto lenrpdone
set vl_val=%vl_val:~1%
set /a vl_vlen+=1
goto chklenrpdone
:lenrpdone
call set %%^^%0%%=%vl_vlen%
set vl_vlen=
set vl_val=
goto :EOF
:done
endlocal
Note a couple of conventions above: the 'return' value of a called subroutine is assigned to an
environment variable with the name of the subroutine.
So for example in the :firstArg subroutine, the line
has the effect of setting an environment variable named firstArg to the first passed parameter. Aside from being exqisitely ugly, that's undocumented usage, as far as I know. So it's not guarenteed to continue to work. But it's a handy idiom, and it seems to work everywhere I care about for now. (w2k-pro .. w2k3-srv), so I may decide to use it somewhere, sometime. Generally though, I'll probably stick with a decidedly less questionable means to accomplish the desired result. That is, simply setting the target variable explicitlycall set %%^^%0%%=%1
:firstArg call set firstArg=%1 goto :eof