I'm busy writing a Windows batch script and I'm having some problem with arguments.
My batch script is the following
#echo off
setlocal EnableDelayedExpansion
:: RETRIEVE ARGS WITH SPACES
set VAR01=%~1
set VAR02=%~2
set VAR03=%~3
:: CONFIRM IT WORKED
echo %VAR01%
echo %VAR02%
echo %VAR03%
endlocal
exit /b
And I am trying to pass it arguments that include spaces.
run_batch.bat "arg var 01" "arg var 02" "arg var 03"
But when it runs I am getting the output
arg var 01
ECHO is off
ECHO is off
Why is it only working correctly for the first argument and how can it be fixed?
NOTE
There was never anything wrong with the code, it seems there were invisible special characters that there causing issues. Must have been the text editor or something along those lines. How do I remove this question?
This should work:
#echo off
setlocal EnableDelayedExpansion
:: RETRIEVE ARGS WITH SPACES
set VAR01=%~1
set VAR02=%~2
set VAR03=%~3
:: CONFIRM IT WORKED
echo %VAR01%
echo %VAR02%
echo %VAR03%
endlocal
exit /b
The wrong part was:
set VAR01=%~1
set VAR01=%~2
set VAR01=%~3
but should be:
set VAR01=%~1
set VAR02=%~2
set VAR03=%~3
A small example script for your perusal.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
REM EXIT IF NO ARGS RECEIVED
IF "%~1"=="" EXIT
REM SET MINIMUM NUMBER OF ARGS
SET "i=1"
REM RETRIEVE ALL ARGS
:ARGS
SET "VAR0%i%=%~1"
SHIFT
IF NOT "%~1"=="" (
SET/A i+=1
GOTO :ARGS
)
REM INFORM NUMBER OF ARGS
CLS
ECHO=THERE WERE %i% ARGS
REM CONFIRM IT WORKED
ECHO=
SET VAR0
REM ALTERNATIVE CONFIRMATION
ECHO=
FOR /L %%A IN (1,1,%i%) DO IF DEFINED VAR0%%A ECHO=!VAR0%%A!
TIMEOUT -1
ENDLOCAL
EXIT
I think there was no problem aside from a typo. A quite tricky way to show the args and put them to numbered vars I learned from DosTips.org (but can't find the exact link) is:
:: Expand-Args.cmd
#Echo off
Set Args=,1=%~1,2=%~2,3=%~3,4=%~4,5=%~5,6=%~6,7=%~7,8=%~8,9=%~9
Set "Args=%*%Args:,="&Set "Arg%"
Set Arg
The batch will have this output run with:
Expand-Args one "2 2" three 4 five 6 "7 7" 8 9
Arg1=one
Arg2=2 2
Arg3=three
Arg4=4
Arg5=five
Arg6=6
Arg7=7 7
Arg8=8
Arg9=9
Args=one "2 2" three 4 five 6 "7 7" 8 9
Edit To not interfere with content in double quotes the delimiter comma might be replaced with an uncommon char like ALT+0127
Related
Trying to obtain an element in a list by its index, using batch script. Here is the code:
#Echo off
setlocal EnableDelayedExpansion
set acc[0]=default
set acc[1]=Account_2
set acc[2]=Account_3
set acc[3]=Account_4
set acc[4]=Account_5
if exist interator.txt (
set /p i=<interator.txt
echo "read: !i!"
echo "!acc[%i%]!"
REM start cmd /c setx AWS_PROFILE !acc[%i%]!
REM start cmd /k python script.py
set /A i=i+1
(echo !i!)>interator.txt
echo "write: !i!"
) else (
(echo 0)>interator.txt
)
Output Received:
"read: 0"
""
"write: 1"
As setx requires the CMD session to be closed, for affect to take place. I am trying a different approach to automate some regular stuff.
Expected Output:
"read: 0"
"default"
"write: 1"
#Echo off
setlocal EnableDelayedExpansion
set "acc[0]=default
set "acc[1]=Account_2"
set "acc[2]=Account_3"
set "acc[3]=Account_4"
set "acc[4]=Account_5"
if exist q65771965.txt (
set /p i=<q65771965.txt
echo "read: !i!"
FOR %%a IN (acc[!i!]) DO (
ECHO "!%%a!"
echo start cmd /c setx AWS_PROFILE "!%%a!"
echo start cmd /k python script.py
)
set /A i=i+1
(echo !i!)
echo "write: !i!"
) else (
(echo 0)
)
GOTO :EOF
OK - small changes to allow this to work on my test environment:
Changed name of file from interator.txt to q65771965.txt (suits my environment)
Removed updating of data file so the modifications are shown on-screen.
Replaced REM start with ECHO start to show the start commands on-screen.
Subtle syntax-oriented change : Use set "var1=data" for setting values - this avoids problems caused by trailing spaces.
Significant change : insert a for loop to transfer indirect values to a metavariable (%%a) and use these.
Possibly-required : I don't use setx much, but I've some memory of the argument's needing to be "quoted"
The problem is, you used echo "%acc[!i!]%" within a codeblock. You need another layer of parsing, like call echo "%%acc[!i!]%%"
As an alternative, restructure your code, so the critical part isn't in a code block:
#Echo off
setlocal EnableDelayedExpansion
set acc[0]=default
set acc[1]=Account_2
set acc[2]=Account_3
set acc[3]=Account_4
set acc[4]=Account_5
if not exist interator.txt (
(echo 0)>interator.txt
goto :eof
)
set /p i=<interator.txt
echo "read: !i!"
echo "%acc[!i!]%"
set /A i=i+1
(echo !i!)>interator.txt
echo "write: !i!"
(this code is functionally identically to yours, just structured in another way)
(btw: it should probably iterator, not interator - but that's only spelling)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
This program stops automatically! When I enter correct name and password (i.e it is not executing from 8th line). Please check if I made any mistakes.
#echo off
:s
set t=0
set /p a= enter your name
set /p b= enter your password
if %a%==andrew(set /a t=%t%+1)
if %b%==123 (set /a t=%t%+2 )
if %t%==3 ( echo welcome andrew
echo ---------------------------------------------------------------------------
:r
set /p c=want to change your settings -yes -no
if %c%==no (echo ok
goto e
)
if %c%==yes (set /p p= what you want to change -name(1) -password(2)
if %p%==1 (set /p a=enter name to change)
if %p%==2 (set /p a=enter password to change)
)
)
if %t%==2 echo %a% is not authorised
if %t%==1 echo enter correct password
if %t%==0 echo Both name and id are wrong enter again
goto r
goto s
:e
pause
Set #echo on and you may see something like this when the script is executed:
--> set t=0
--> set /p a= enter your name
enter your nameandrew
--> set /p b= enter your password
enter your password123
--> if andrew == andrew(set /a t=0+1)
--> if 123 == 123 (set /a t=0+2 )
(echo was unexpected at this time.
--> if ==no (echo ok
The script stopped with error:
(echo was unexpected at this time.
Line 7 showed OK:
if 123 == 123 (set /a t=0+2 )
though, next command output seen is:
if ==no (echo ok
which means %c% is undefined as you can see a value missing left of the == that caused the error.
This could be happening as to the opening parentheses in line 8:
if %t%==3 ( echo welcome andrew
The parser will see the opening parentheses and will keep reading the lines until it reaches a closing parentheses. It will substitute the percentage enclosed variables before execution. This makes %c% undefined as %c% is not defined before the opening parentheses on line 8 began the read.
So it seems the parser read:
if %t%==3 ( echo welcome andrew
echo ---------------------------------------------------------------------------
:r
set /p c=want to change your settings -yes -no
if %c%==no (echo ok
goto e
)
And does variable substitution of known values of t=3 and c=:
if 3==3 ( echo welcome andrew
echo ---------------------------------------------------------------------------
:r
set /p c=want to change your settings -yes -no
if ==no (echo ok
goto e
)
The error of if ==no (echo ok is shown.
Your question in the title:
why i cant get output from this batch script?
It is the opening parentheses in line 8. So one should ask thy self... What is that opening parentheses doing there in line 8 when it will include a label :r and a set /p that sets a variable c that can cause an undefined variable?
If the opening parentheses is meant to be there, view set /? that may show an example like:
set VAR=before
if "%VAR%" == "before" (
set VAR=after
if "%VAR%" == "after" #echo If you see this, it worked
)
The example code and the details in set /? informs you how set variables work.
The label between the parentheses would still be a concern as it can cause error in a read block of code.
There is a couple of things. Do not set single character variables or label names. rather use set vara than using set a but making things recognisable like using variables that means something, makes things less confusing.
when matching variables with a value using == double quote each side. i.e if "%var" == "123"
For selection, use choice instead of set /p
Also, when using set /a the better way to add is set /a vart+=1 instead of set /a %vart%+1
This should do more or less what you want.
#echo off
:start
set vart=0
set /p "name=Enter your name: "
set /p "pass=Enter your password: "
if /i "%name%" == "andrew" set /a vart+=1
if "%pass%" == "123" set /a vart+=2
if %vart% equ 3 echo welcome andrew
echo ---------------------------------------------------------------------------
:settings
choice /c YN /m "Change your settings?"
if %errorlevel% equ 0 goto opt0
goto opt%errorlevel%
:opt0
echo You Pressed CTRL+C and selected "N"
exit /b 1
:opt2
pause
goto :eof
:opt1
choice /c UP /m "Change your User or Password?"
if %errorlevel% equ 0 goto opt0
goto chng%errorlevel%
:chng2
set /p "name=Enter name to change: "
goto :eof
:chng1
set /p "pass=Enter password to change: "
if %vart% equ 2 echo %a% is not authorised
if %vart% equ 1 echo enter correct password
if %vart% equ 0 echo Both name and id are wrong enter again
goto settings
goto start
Please read the syntax of the commands, such as if and set before posting.
Addressing some problems in your code:
First,
if %a%==andrew(set /a t=%t%+1)
is intercepted by cmd.exe as:
if %a% is andrew(set, run a command called /a t=%t%+1. Also, please double quote all set /p ...= in your script, even though it can't handle poison characters.
Second, you have unescaped closing parenthesis ) in your if, which can cause interception errors.
I am writing a small console application. This app should print a help when -h is passed. This is what I'm trying to get:
Program that prints help.
Usage:
prg [OPTIONS]
Options:
-h - print help.
-x - some very very very very very very very very very very very very very very
very very very very long parameter description.
-y - another very very very very very very very very very very very very very
very very very very long parameter description.
I'm using echo. -x - {long description here} to print parameter description. And of course when the string is too long it is printed like this:
-x - some very very very very very very very very very very very very
very very very very long parameter description.
So the question is how can I print a long string with something like left border specified?
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
for /f %%I in ('powershell ^(Get-Host^).UI.RawUI.WindowSize.width') do set width=%%I
SET "spaces= "
:splp
IF "!spaces:~%width%!"=="" SET spaces=%spaces%%spaces%&GOTO splp
CALL :display 0 "Program that prints help."
CALL :display 0 "Usage:"
CALL :display 2 "prg [OPTIONS]"
CALL :display 0
CALL :display 0 "Options:"
CALL :display 2 "-x" 5 "- some very very very very very very very very very very very very very very very very very very long parameter description."
CALL :display 2 "-y" 5 "- another very very very very very very very very very very very very very very very very very long parameter description."
GOTO :EOF
:display
SET "remainder="
SET "line=!spaces:~0,%1!"
IF "%~2"=="" GOTO write
IF "%3"=="" SET "line=%line%%~2"&GOTO write
SET /a indent=%1+%3
SET "remainder=%~2%spaces%"
SET "line=%line%!remainder:~0,%3!
SET "remainder=%~4"
:remainlp
SET /a eol=width-indent-1
:seteollp
SET "eolchar=!remainder:~%eol%,1!"
IF NOT DEFINED eolchar SET "line=%line%%remainder%"&set "remainder="&goto write
IF NOT "%eolchar%"==" " set /a eol-=1&GOTO seteollp
SET "line=%line%!remainder:~0,%eol%!
SET /a eol+=1
set "remainder=!remainder:~%eol%!"
:write
ECHO(%line%
IF NOT DEFINED remainder GOTO :EOF
SET "line=!spaces:~0,%indent%!"
GOTO remainlp
I'll claim no originality for the magic powershell line - it came from Rojo's response here
Have you tried using tabs?
For example:
echo.^ -x - some very very very very very very very very very very
echo.^ ^ very very very very very very very
echo.^ ^ very long parameter description.
I've used the '^' character to show where the tabs are and had to split the message over a few lines to stop the word wrapping.
Here is the script I just tried (option x uses tabs and option y does not)
#echo OFF
echo. Usage:
echo. prg [OPTIONS]
echo.
echo. Options:
echo. -h - print help.
echo. -x - some very very very very very very very very very very
echo. very very very very very very very
echo. very long parameter description.
echo. -y - another very very very very very very very very very very very very very
echo. very very very very long parameter description.
And I get the following output (option x uses tabs and option y does not)
C:\Temp>usage.bat
Usage:
prg [OPTIONS]
Options:
-h - print help.
-x - some very very very very very very very very very very
very very very very very very very
very long parameter description.
-y - another very very very very very very very very very very very
very very
very very very very long parameter description.
echo off
setlocal enabledelayedexpansion
set "textx=some very very very very very very very very very very very very very very very very very very long parameter description."
REM find screen widht (subtract 8 for a leading tab and two for a right "border" :
for /f "tokens=2 delims=: " %%i in ('mode^|find "Columns"') do set /a colums=%%i-10
call :wrap "-x" "%textx%"
call :wrap "-y" "another quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite quite long parameter description."
exit /b
:wrap <option> <text>
set "text=%~2"
set /p ".=%~1"<nul
for /l %%i in (0,%colums%,1000) do echo. !text:~%%i,%colums%!|findstr ".."
goto :eof
NOTE: there is a between the echo. and !text....
You may have to replace Columns in find "Columns" with the correct string (it's language dependent)
I subtracted 10 from the screen width: 8 for the leading and two to end the line two chars before the windows ends.
1000 is the max length of the string. Adapt as needed. (the higher the value, the slower the output)
|findstr ".." supresses the output of empty lines
This has a few disadvantages:
there is no space in front of -x (set does not support it)
It will wrap at the end of the line even if it's in the middle of a word. Doing a "word wrap" could be done, but with a lot more code.
It has problems with special chars (for example &, !, |)
Just converting some shell scripts into batch files and there is one thing I can't seem to find...and that is a simple count of the number of command line arguments.
eg. if you have:
myapp foo bar
In Shell:
$# -> 2
$* -> foo bar
$0 -> myapp
$1 -> foo
$2 -> bar
In batch
?? -> 2 <---- what command?!
%* -> foo bar
%0 -> myapp
%1 -> foo
%2 -> bar
So I've looked around, and either I'm looking in the wrong spot or I'm blind, but I can't seem to find a way to get a count of number of command line arguments passed in.
Is there a command similar to shell's "$#" for batch files?
ps. the closest i've found is to iterate through the %1s and use 'shift', but I need to refernece %1,%2 etc later in the script so that's no good.
Googling a bit gives you the following result from wikibooks:
set argC=0
for %%x in (%*) do Set /A argC+=1
echo %argC%
Seems like cmd.exe has evolved a bit from the old DOS days :)
You tend to handle number of arguments with this sort of logic:
IF "%1"=="" GOTO HAVE_0
IF "%2"=="" GOTO HAVE_1
IF "%3"=="" GOTO HAVE_2
etc.
If you have more than 9 arguments then you are screwed with this approach though. There are various hacks for creating counters which you can find here, but be warned these are not for the faint hearted.
The function :getargc below may be what you're looking for.
#echo off
setlocal enableextensions enabledelayedexpansion
call :getargc argc %*
echo Count is %argc%
echo Args are %*
endlocal
goto :eof
:getargc
set getargc_v0=%1
set /a "%getargc_v0% = 0"
:getargc_l0
if not x%2x==xx (
shift
set /a "%getargc_v0% = %getargc_v0% + 1"
goto :getargc_l0
)
set getargc_v0=
goto :eof
It basically iterates once over the list (which is local to the function so the shifts won't affect the list back in the main program), counting them until it runs out.
It also uses a nifty trick, passing the name of the return variable to be set by the function.
The main program just illustrates how to call it and echos the arguments afterwards to ensure that they're untouched:
C:\Here> xx.cmd 1 2 3 4 5
Count is 5
Args are 1 2 3 4 5
C:\Here> xx.cmd 1 2 3 4 5 6 7 8 9 10 11
Count is 11
Args are 1 2 3 4 5 6 7 8 9 10 11
C:\Here> xx.cmd 1
Count is 1
Args are 1
C:\Here> xx.cmd
Count is 0
Args are
C:\Here> xx.cmd 1 2 "3 4 5"
Count is 3
Args are 1 2 "3 4 5"
Try this:
SET /A ARGS_COUNT=0
FOR %%A in (%*) DO SET /A ARGS_COUNT+=1
ECHO %ARGS_COUNT%
If the number of arguments should be an exact number (less or equal to 9), then this is a simple way to check it:
if "%2" == "" goto args_count_wrong
if "%3" == "" goto args_count_ok
:args_count_wrong
echo I need exactly two command line arguments
exit /b 1
:args_count_ok
Avoids using either shift or a for cycle at the cost of size and readability.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set /a arg_idx=1
set "curr_arg_value="
:loop1
if !arg_idx! GTR 9 goto :done
set curr_arg_label=%%!arg_idx!
call :get_value curr_arg_value !curr_arg_label!
if defined curr_arg_value (
echo/!curr_arg_label!: !curr_arg_value!
set /a arg_idx+=1
goto :loop1
)
:done
set /a cnt=!arg_idx!-1
echo/argument count: !cnt!
endlocal
goto :eof
:get_value
(
set %1=%2
)
Output:
count_cmdline_args.bat testing more_testing arg3 another_arg
%1: testing
%2: more_testing
%3: arg3
%4: another_arg
argument count: 4
EDIT: The "trick" used here involves:
Constructing a string that represents a currently evaluated command-line argument variable (i.e. "%1", "%2" etc.) using a string that contains a percent character (%%) and a counter variable arg_idx on each loop iteration.
Storing that string into a variable curr_arg_label.
Passing both that string (!curr_arg_label!) and a return variable's name (curr_arg_value) to a primitive subprogram get_value.
In the subprogram its first argument's (%1) value is used on the left side of assignment (set) and its second argument's (%2) value on the right. However, when the second subprogram's argument is passed it is resolved into value of the main program's command-line argument by the command interpreter. That is, what is passed is not, for example, "%4" but whatever value the fourth command-line argument variable holds ("another_arg" in the sample usage).
Then the variable given to the subprogram as return variable (curr_arg_value) is tested for being undefined, which would happen if currently evaluated command-line argument is absent. Initially this was a comparison of the return variable's value wrapped in square brackets to empty square brackets (which is the only way I know of testing program or subprogram arguments which may contain quotes and was an overlooked leftover from trial-and-error phase) but was since fixed to how it is now.
A reasonably robust solution is to delegate counting to a subroutine invoked with call; the subroutine uses goto statements to emulate a loop in which shift is used to consume the (subroutine-only) arguments iteratively:
#echo off
setlocal
:: Call the argument-counting subroutine with all arguments received,
:: without interfering with the ability to reference the arguments
:: with %1, ... later.
:: NOTE: See comments re /? below.
call :count_args %* >NUL || (echo Usage: ... & exit /b 0)
:: Print the result.
echo %ReturnValue% argument(s) received.
:: Exit the batch file.
exit /b
:: Subroutine that counts the arguments given.
:: Returns the count in %ReturnValue%
:count_args
set /a ReturnValue = 0
:count_args_for
if %1.==. goto :eof
set /a ReturnValue += 1
shift
goto count_args_for
Limitations:
/? among the arguments - except if passed as "/?" - is not supported, because call invariably interprets it as a request to show its command-line help.
The code above detects this case (curiously, call signals failure via its exit code after showing its help) and interprets this case as wanting to show the current batch file's help instead of counting arguments.
Arguments with metacharacters (special characters) are supported, if enclosed in "..." (as is typical), but character-individual ^-escaping in an unquoted argument does not work (because the ^ is already stripped by the time the batch file sees the arguments, and expanding %* then causes the metacharacters to act as such):
:: OK - arguments with special chars. are "..."-enclosed.
:: -> "3 argument(s) received."
test.cmd hi "you & me" "a<b"
:: !! FAILS
:: The last argument uses character-individual ^-escaping.
:: -> error "The system cannot find the file specified."
test.cmd hi "you & me" a^<b
A syntactically invalid argument list is not supported (which is arguably not a problem):
:: !! FAILS
:: Syntactically invalid argument list
:: -> error "The syntax of the command is incorrect."
test.cmd a"
The last answer was two years ago now, but I needed a version for more than nine command line arguments. May be another one also does...
#echo off
setlocal
set argc_=1
set arg0_=%0
set argv_=
:_LOOP
set arg_=%1
if defined arg_ (
set arg%argc_%_=%1
set argv_=%argv_% %1
set /a argc_+=1
shift
goto _LOOP
)
::dont count arg0
set /a argc_-=1
echo %argc_% arg(s)
for /L %%i in (0,1,%argc_%) do (
call :_SHOW_ARG arg%%i_ %%arg%%i_%%
)
echo converted to local args
call :_LIST_ARGS %argv_%
exit /b
:_LIST_ARGS
setlocal
set argc_=0
echo arg0=%0
:_LOOP_LIST_ARGS
set arg_=%1
if not defined arg_ exit /b
set /a argc_+=1
call :_SHOW_ARG arg%argc_% %1
shift
goto _LOOP_LIST_ARGS
:_SHOW_ARG
echo %1=%2
exit /b
The solution is the first 19 lines and converts all arguments to variables in a c-like style. All other stuff just probes the result and shows conversion to local args. You can reference arguments by index in any function.
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).
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%
#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.
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.
#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.
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