20

Further to How to Pass Command Line Parameters in batch file how does one get the rest of the parameters with specifying them exactly? I don't want to use SHIFT because I don't know how many parameters there might be and would like to avoid counting them, if I can.

For example, given this batch file:

@echo off
set par1=%1
set par2=%2
set par3=%3
set therest=%???
echo the script is %0
echo Parameter 1 is %par1%
echo Parameter 2 is %par2%
echo Parameter 3 is %par3%
echo and the rest are %therest%

Running mybatch opt1 opt2 opt3 opt4 opt5 ...opt20 would yield:

the script is mybatch
Parameter 1 is opt1
Parameter 2 is opt2
Parameter 3 is opt3
and the rest are opt4 opt5 ...opt20

I know %* gives all the parameters, but I don't wan't the first three (for example).

Community
  • 1
  • 1
matt wilkie
  • 17,268
  • 24
  • 80
  • 115
  • possible duplicate of [Is there a way to indicate the last n parameters in a batch file?](http://stackoverflow.com/questions/761615/is-there-a-way-to-indicate-the-last-n-parameters-in-a-batch-file) – matt wilkie Jul 15 '15 at 19:50

5 Answers5

27

Here's how you can do it without using SHIFT:

@echo off

for /f "tokens=1-3*" %%a in ("%*") do (
    set par1=%%a
    set par2=%%b
    set par3=%%c
    set therest=%%d
)

echo the script is %0
echo Parameter 1 is %par1%
echo Parameter 2 is %par2%
echo Parameter 3 is %par3%
echo and the rest are %therest%
Patrick Cuff
  • 28,540
  • 12
  • 67
  • 94
  • 1
    This probably won't handle parameters enclosed in quotation marks correctly. – Joey Apr 17 '09 at 19:05
  • 1
    Johannes left a solid answer on this question: http://stackoverflow.com/questions/761615/is-there-a-way-to-indicate-the-last-n-parameters-in-a-batch-file – Dave Apr 17 '09 at 23:34
  • 1
    @Dave; what's not solid abut this answer? – Patrick Cuff Apr 19 '09 at 12:34
  • @PatrickCuff If you run your batch file with quoted parameters with spaces in them, the output will be wrong. Try these 6 for example: "param 1" "param 2" "param 3" 4 5 6 – dave-holm Sep 23 '11 at 02:26
  • Both have multiple issues. Try to forward parameters like `--path=c:\a\b\c`. See [my answer](https://stackoverflow.com/a/49040793/468725) that handles these these cases better. – Pavel P Mar 01 '18 at 00:24
5
@echo off
setlocal enabledelayedexpansion

set therest=;;;;;%*
set therest=!therest:;;;;;%1 %2 %3 =!

echo the script is %0
echo Parameter 1 is %1
echo Parameter 2 is %2
echo Parameter 3 is %3
echo and the rest are: %therest%

This works with quoted arguments and with arguments that have equal signs or commas, as long as first three arguments didn't have these special delimiter chars.

Sample output:

test_args.bat "1 1 1" 2 3 --a=b "x y z"
Parameter 1 is "1 1 1"
Parameter 2 is 2
Parameter 3 is 3
and the rest are: --a=b "x y z"

This works by replacing %1 %2 %3 in original command line %*. The first five semicolons are there just to make sure that only the first occurrence of these %1 %2 %3 is replaced.

Kevin Fegan
  • 1,292
  • 1
  • 16
  • 29
Pavel P
  • 15,789
  • 11
  • 79
  • 128
  • On the off chance that there are more than 1 space b/w the parameters to be skipped (i.e. %1 %2 %3 in this example) this could also fail (since the string replacement would not work). In other words, for this to work as expected, the batch should be called properly, with a single space b/w parameters. Also, first 3 parameters should not contain an equal sign. – OzgurH Jul 21 '22 at 23:09
1

The following code uses shift, but it avoids to parse the command line using for and lets the command line interpreter do this job (regard that for does not parse double-quotes properly, for instance argument set A B" "C is interpreted as 3 arguments A, B", "C by for, but as 2 arguments A, B" "C by the interpreter; this behaviour prevents quoted path arguments like "C:\Program Files\" from being handled correctly):

@echo off

set "par1=%1" & shift /1
set "par2=%1" & shift /1
set "par3=%1" & shift /1

set therest=
set delim=

:REPEAT
if "%1"=="" goto :UNTIL
set "therest=%therest%%delim%%1"
set "delim= "
shift /1
goto :REPEAT
:UNTIL

echo the script is "%0"
echo Parameter 1 is "%par1%"
echo Parameter 2 is "%par2%"
echo Parameter 3 is "%par3%"
echo and the rest are "%therest%"
rem.the additional double-quotes in the above echoes^
    are intended to visualise potential whitespaces

The remaining arguments in %therest% might not look like the way they were originally concerning the delimiters (remember the command line interpreter also treats TABs, ,, ;, = as delimiters as well as all combinations), because all delimiters are replaced by a single space here. However, when passing %therest% to some other command or batch file, it will be parsed correctly.

The only limitation I encountered so far applies to arguments containing the caret character ^. Other limitations (related to <, >, |, &, ") apply to the command line interpreter itself.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • +1 because it works for the stuff I have on hand, and it doesn't delve into what I might call batch voodoo. It is still more verbose than the other answer though and a bit harder to see how it works. Can you expand on the alluded circumstances where this will handle things better than FOR? – matt wilkie Jul 15 '15 at 04:21
  • 1
    Thanks! The `FOR` implementation features only space and tabs as delimiters while the command line interpreter also allows `, ; =` (and there might still be more?). You should replace the portion `"tokens=1-3*"` with `"tokens=1-3* delims=,;=_ "` to reflect that (the `_` stands for a TAB). Anyway, the interpreter does not treat a double-quoted delimiter character (e. g., `" "`, `","`) as a delimiter, but `FOR` does; so the arguments `A B" "C` are interpreted as 2 arguments `A` and `B" "C` by the interpreter, and as 3 arguments `A` and `B"` and `"C` by the `FOR` implementation. – aschipfl Jul 15 '15 at 18:34
  • Thanks for the explication. I finally read (with attention) the link to near duplicate http://stackoverflow.com/questions/761615/is-there-a-way-to-indicate-the-last-n-parameters-in-a-batch-file and see your answer is a fleshed out version of @Joey's over there. Readers should read the other answers too; there's another take on using `for` that could be useful – matt wilkie Jul 15 '15 at 19:53
  • I encountered another difference in behaviour between the command line interpreter and the `FOR` variant: the latter absorbs twice as much adjacent carets `^` if such are contained in the arguments: when using the interpreter method, you need at least 2 `^^` to get `^` in the output, `^^^^` to get `^^`, etc.; with `FOR`, you need at least `^^^^` to get `^`,etc. – aschipfl Jul 15 '15 at 22:55
1
@ECHO OFF
SET REST=
::# Guess you want 3rd and on.
CALL :SUBPUSH 3 %*
::# ':~1' here is merely to drop leading space.
ECHO REST=%REST:~1%
GOTO :EOF

:SUBPUSH
SET /A LAST=%1-1
SHIFT
::# Let's throw the first two away.
FOR /L %%z in (1,1,%LAST%) do (
  SHIFT
)
:aloop
SET PAR=%~1
IF "x%PAR%" == "x" (
  GOTO :EOF
)
ECHO PAR=%PAR%
SET REST=%REST% "%PAR%"
SHIFT
GOTO aloop
GOTO :EOF

I like to use subroutines instead of EnableDelayedExpansion. Above is extract from my dir/file pattern processing batch. Don't say this cannot handle arguments with =, but at least can do quoted path with spaces, and wildcards.

Ntats
  • 1
  • 4
1

One more alternative :-)

some of the other answers did not deal with quotes, or with extra spaces between parameters. Disclaimer: I have not tested this extremely well.

This is all just a workaround for %* not being updated after a shift ugg!

I implemented it as a POP starting from the technique by @Pavel-P.

  • It relies on the batch parser to remove extra spaces
  • It depends on a global variable 'Params'
  • ~ removes quotes -- leaving it up to you to re-quote as desired. Remove the ~ if you don't want that.
:PopFirstParam
    @set varname=%~1
    @set %varname%=%~2
    @rem @echo %varname% = '!%varname%!'
    @if '%3' equ '' set Params=&goto :eof
    @call :popone %Params%
@goto :eof
:popone
    @set Params=;;;%*
    @set Params=!Params:;;;%1 =!
@goto :eof

Usage:

@setlocal enabledelayedexpansion
@set Params=%*
@call :PopFirstParam P1 %Params%
@call :PopFirstParam P2 %Params%
@call :PopFirstParam P3 %Params%
@call :PopFirstParam P4 %Params%
@rem etc . . .

Specifically I use it to optionally run commands asynchronously:

@setlocal enabledelayedexpansion
@rem set variables that decide what to be run  . . . . 

@call :RunIfDefined doNum1 "Title 1"      mycmd1.exe     some  "params"   here
@call :RunIfDefined doNum2 "MyTitle 2"    mylongcmd2.exe some  "params"   here
@call :RunIfDefined doNum3 "Long Title 3" mycmd3.exe     some  "params"   here
@goto :eof

:RunIfDefined
    @set Params=%*
    @call :PopFirstParam DefineCheck %Params%
    @if not defined %DefineCheck% goto :eof
    
    @call :PopFirstParam WindowTitle %Params%
    @call :PopFirstParam Command %Params%
    @rem %Params% should now be the remaining params (I don't care about spaces here)
    @echo start "%WindowTitle%" "%Command%" %Params%
    @start "%WindowTitle%" "%Command%" %Params%
@goto :eof
C:\test> set doNum1=yes
C:\test> set doNum3=yes
C:\test> call dothings.bat

start "Title 1" "mycmd1.exe"     some  "params"   here
start "Long Title 3" "mycmd3.exe"     some  "params"   here

too bad DOS doesn't have a #include

Thomas Oatman
  • 301
  • 2
  • 9