I'm trying to create a command in the command prompt, but use arguments like you can do with a function in another language.
For example, you can create a batch file in the C:\Windows\System32 folder and execute it in the command prompt by typing its name, creating a "command." This is very simple, though, and simply runs whatever the batch file contains.
My command I made is called toolbx, which has different useful shortcuts in the command prompt. But, I want to execute different tools by typing something like toolbx 1. Here's what it might look like.
toolbx [input received changing what tool you want to use] (e.g. 1)
In toolbx.bat
if %inputRecieved% == 1 goto 1
I'm not the best at explaining, so another example in Python might be:
def functionWithArgs (tool)
Executed as:
functionWithArgs(1)
As another summary:
I'm trying to make a command used in the command prompt with a batch file
I want the batch file to have arguments defined when the command is run (like the python example) that can be used as variables in the batch file.
Thank you in advance!
It is your lucky day today, windows batch files do in fact support a feature called command-line parameters. Here is what you need to do:
set inputRecieved=%1
if "%inputRecieved%"=="1" goto label1
goto :eof
:label1
blabla...
Have fun coding!
P.S.
Please note that the correct spelling of the word is "received", not "recieved".
You can use the macro style invented by #jeb, #dbenham and DosTips user #Ed Dyreen:
Definitions:
#echo off
SETLOCAL DISABLEDELAYEDEXPANSION
::Definitions
( set LF=^
%= EMPTY =%
)
set ^"NL=^^^%LF%%LF%^%LF%%LF%^^"
The whole purpose is to create a multiline variable. See the batch newline variable hack
Main Macro:
::Macro
ENDLOCAL &^
set $MACRO.Toolbx=FOR %%a in (args main) do if "%%a" == "main" (%NL%
for %%A in (%payload%) do (%NL%
%= MAIN MACRO HERE, END LINES WITH NL =%%NL%
)%NL%
) ELSE SETLOCAL ENABLEDELAYEDEXPANSION ^& set args=,
exit /b
Save this as MACRO.Toolbx.bat. To use the macro inside your batch file:
call MACRO.Toolbx.bat
%$MACRO.Toolbx% COMMAND_LINE_ARGS
The macro uses a clever FOR loop to "catch" the arguments. Also, in order to catch arguments, DISABLEDELAYEDEXPANSION is necessary, which is inseparable from SETLOCAL. However, ENDLOCAL will destroy all user-defined variable in that range, but we need the newline definitions. Therefore, ENDLOCAL and the macro definition must be on the same line. This technique is called ENDLOCAL tunneling.
sure
[Complete path and file name] [Parameter1] [Parameter2] [and_so_on]
Is a space that separates parameters that can be expanded with %1 %2 %3...
You can expand all parameters with %* or up to 9 parameters individually
for example:
%windir%\system32\toolbx.cmd Tool_1
note the parameter beign passed is: Tool_1
in the example below is beign used at line 2 where %1 expands: Tool_1
setlocal EnableDelayedExpansion
set tool=%1
set argument_1=Have
set argument_2=a
set argument_3=Nice
set argument_4=Day
CALL :Function_%tool% argument_1 argument_2 argument_3 argument_4
:: after Function_Tool_1 ends by reading the next GOTO :EOF after its Label,
:: will return to read instructions after this line
::
pause&&exit
::
:Function_Tool_1
echo. %argument_1% %argument_2% %argument_3% %argument_4%
goto :EOF
::
::
NOTE:
When using CALL to call labels, you have to use a colon for example:
CALL :YourLabel
And after the label all the parameters or arguments separated with a space
for example:
CALL :YourLabel param1 param2 param3 parampampam etc
I think the syntax for labels is the same as for variables.
You have to use a GOTO :EOF to end a function or procedure
See you don't use a colon when going to the label using GOTO for example:
GOTO YourLabel
but when using CALL is different, that is explained better typing:
EXIT /?
GOTO /?
& is used to separate instructions
&& is used to separate instructions inside a batch file as toolbx.cmd
Also note in the example above:
%1 %2 %3 and %4 along the function could expand the variable name, for example:
echo. %1 %2 %3 %4
would echo: argument_1 argument_2 argument_3 argument_4
and using SETLOCAL EnableDelayedExpansion
!%1! could expand the value of the variable, for example:
echo. !%1! !%2! !%3! !%4!
would echo: Have a Nice Day
A technique has been developed using for loops that allows arguments to be captured when a variable is expanded - Batch Macros.
An outer for loop is used, which on the first iteration assigns a variable containing the argument values, and the inner loop/s proccess and act upon those values.
#echo off
Set "Macroname=For %%n in (1 2)Do if %%n==2 (Echo(!Args!)Else Set args="
Setlocal EnableDelayedExpansion
%Macroname% Hello World
The technique requires defining the macro variable in an environment where Delayed expansion is disabled, then using Setlocal EnableDelayedExpansion to enable the macro. ( Unless of course all ! expansion symbols are correctly escaped during macro definition for an environment where delayed expansion is enabled. )
Below is a template for batch macro's that builds an Args array and and proccesses switches that trail arguments. The purpose of the template is to allow quick scripting of advanced functions by having all the argument and switch handling logic ready to go, so that the meat of the function is all that needs to be scripted.
Args are returned to the array $Macroname_args[!$Macroname_args[i]!] where:
!$Macroname_args[i]! is a 0 indexed count of args.
Switches are returned in associated variables using the switch string as the index IE:
%MacroName% /A
Results in: !$Macroname_Switch[A]!==true
Or if a subarg is supplied:
%MacroName% /A sub arg
Results in: !$Macroname_Switch[A]!==sub arg
The macro uses Substring modification to seperate switches from args and process switches.
As such, arg or switch values containing the following characters:
= * / "
should be supplied in a substituted form:
{EQ} {AS} {FS} {DQ}, which are replaced using substitution built into the macro.
#Echo off
(Set \n=^^^
%= \n macro newline variable. Do not modify =%)
(Set LF=^
%= LF newline variable. Do not modify =%)
:# Example Switches for demonstrating concept. List used twice within macro switch handling. Define outside macro to
:# simplify maintainance.
Set $MacroName_Switches="A" "B" "C" "D" "E" "Def"
Set $MacroName=For %%n in (1 2)Do if %%n==2 (%\n%
For /F "Tokens=1,2 Delims==" %%G in ('Set "$Macroname_Arg[" 2^^^> nul')Do Set "%%~G="%\n%
For %%G in ( %$MacroName_Switches% )Do Set "$MacroName_Switch[%%~G]="%\n%
If not "!$MacroName_args:* /=!" == "!$MacroName_args!" (%\n: build $MacroName.Args[!$MacroName_arg[i]!] array if args present =%
Set "$MacroName_leading.args=!$MacroName_args:*/=!"%\n%
For /F "Delims=" %%G in ("!$MacroName_leading.args!")Do Set "$MacroName_leading.args=!$MacroName_args:/%%G=!"%\n%
Set ^"$MacroName_args=!$MacroName_args:"=!"%\n%
Set "$MacroName_arg[i]=0"%\n%
For %%G in (!$MacroName_leading.args!)Do (%\n%
Set /A "$MacroName_arg[i]+=1"%\n%
Set "$MacroName_arg[!$MacroName_arg[i]!]=%%~G"%\n%
For %%i in ("!$MacroName_arg[i]!")Do (%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{FS}=/!"%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{AS}=*!"%\n%
Set "$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{EQ}==!"%\n%
Set ^"$MacroName_arg[%%~i]=!$MacroName_arg[%%~i]:{DQ}="!"%\n%
)%\n%
)%\n%
) Else (%\n: remove doublequotes from args before switch processing switches =%
Set ^"$MacroName_args=!$MacroName_args:"=!"%\n%
)%\n%
Set "$MacroName_LastSwitch="%\n%
For /L %%L in (2 1 4)Do If "!$MacroName_LastSwitch!" == "" (%\n%
If "!$MacroName_Args:~-%%L,1!" == " " Set "$MacroName_LastSwitch=_"%\n%
If "!$MacroName_Args:~-%%L,1!" == "/" (%\n: Flag last switch true if no subargs; Works for character switches of up to 3 characters =%
For /F "Delims=" %%v in ('Set /A "%%L-1"')Do Set "$MacroName_Switch[!$MacroName_Args:~-%%v!]=true"%\n%
If not "!$MacroName_Args:/?=!." == "!$MacroName_Args!." Set "$MacroName_Switch[help]=true"%\n%
Set "$MacroName_Args=!$MacroName_Args:~0,-%%L!"%\n%
Set "$MacroName_LastSwitch=_"%\n%
)%\n%
)%\n%
For %%G in ( %$MacroName_Switches% )Do If not "!$MacroName_args:/%%~G =!" == "!$MacroName_args!" (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Args:*/%%~G =!"%\n%
If not "!$MacroName_Switch[%%~G]:*/=!" == "!$MacroName_Switch[%%~G]!" (%\n%
Set "$MacroName_Trail[%%~G]=!$MacroName_Switch[%%~G]:*/=!"%\n%
For %%v in ("!$MacroName_Trail[%%~G]!")Do (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]: /%%~v=!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:/%%~v=!"%\n%
)%\n%
Set "$MacroName_Trail[%%~G]="%\n%
If "!$MacroName_Switch[%%~G]:~-1!" == " " Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:~0,-1!"%\n%
If "!$MacroName_Switch[%%~G]!" == "" Set "$MacroName_Switch[%%~G]=true"%\n%
If not "!$MacroName_Switch[%%~G]!" == "" If not "!$MacroName_Switch[%%~G]!" == "true" (%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{FS}=/!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{AS}=*!"%\n%
Set "$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{EQ}==!"%\n%
Set ^"$MacroName_Switch[%%~G]=!$MacroName_Switch[%%~G]:{DQ}="!"%\n%
)%\n%
)%\n%
)%\n: Insert Switch Assessment Below. Use conditional testing of valid switches to enact macro functions before $MacroName_args assignment =%
REM INSERT COMMANDS BETWEEN THE ABOVE AND BELOW LINES TERMINATING EACH WITH \n VARIABLE %\n%
%= EXAMPLE. Remove this line. =% Echo(!LF!Example: %%$Macroname%%!$Macroname_args!%\n%
%= EXAMPLE. Remove this line. =% Set $Macroname_Arg[%\n%
%= EXAMPLE. Remove this line. =% Set $Macroname_Switch[%\n%
%= ESCAPE AMPERSANDS AND REDIRECTION CHARACTERS DURING MACRO DEFINITION. =%) Else Set $MacroName_args=
Setlocal enableDelayedExpansion
:# Proof of concept
%$Macroname% "{AS} param 1 {EQ} {DQ}https:{FS}{FS}stackoverflow.com{FS}posts{FS}60793679{DQ}" "param 2" /C one /D 12 /Def
%$Macroname% /B a b /E /Def "hello world & ^! > < | %%" /A 1 + 2 {EQ} 3 /Undefined switch
Related
I have written small script for converting text to uppercase as shown below and saved this file as .BAT extension
`converttoupper.bat`
I want user to try "help" command, so that they get the help on syntax for using the commands as shown below
help converttoupper
something like this
# help converttoupper
For more information on a specific command, type HELP command-name
CONVERTTOUPPER This converts the text to upper case
Update
I am fine even if I get something as shown below. I do not want to overwrite any windows command.
helpme converttoupper
or
helpme connectvpn
I have many BAT files, and wish to display respective helps when each executed.
You can create a "fake" function. Let's call this define.cmd and place it in %systemroot%\system32
We add the code:
#echo off
for /f "tokens=1,*delims=? " %%i in ('type "%~1" ^|findstr ":?"') do echo %%j
Then in all your batch files you want people to read the help for, add the help lines by starting them off with a :? using your convertoupper.cmd file as example:
#echo off & set upper=
if "%~1" == "" echo incorrect usage & call define.cmd "%0"
if "%~1" == "/?" call define.cmd "%0"
for /f "skip=2 delims=" %%I in ('tree "\%~1"') do if not defined upper set "upper=%%~I"
set "upper=%upper:~3%"
echo %upper%
goto :eof
:? # help converttoupper
:? "define %0" or "%0 /?" will display this help content
:? For more information on a specific command, type HELP command-name
:? CONVERTTOUPPER This converts the text to upper case
Now you can run define converttoupper or converttoupper /?. If you run converttoupper without any arguments, it will also display the same help.
Below is an example of safely handling arg capture and help enqueries.
After arguments are safely Captured, Findstr is used to test the content for valid help switches:
Set Args | %SystemRoot%\System32\Findstr.exe /bli "Args=\/? Args=-? Args=Help?" > nul && (Rem commands)
Set Args : allows the argument string to be piped to findstr without any risk of failure due to poison characters.
/bli : findstr sawitches : match literal string at beginning of line ignoring case.
"Args=\/? Args=-? Args=Help?" : Space delimited list of strings to match; treated as match string a or b or c
> nul : Suppress the output of any match
&& : Conditional operator; 'On command success'
Note: Terminating each help switch with ? allows use of substring modification to remove the leading switch and space and directly Call a label prefixed with the query keyword
#Echo off & SETLOCAL
=========================================================================
Rem -- Arg capture method is a modified version of Dave Benhams method:
Rem -- https://www.dostips.com/forum/viewtopic.php?t=4288#p23980
SETLOCAL EnableDelayedExpansion
1>"%~f0:Params.dat" <"%~f0:Params.dat" (
SETLOCAL DisableExtensions
Set prompt=#
Echo on
For %%a in (%%a) do rem . %*.
Echo off
ENDLOCAL
Set /p "Args="
Set /p "Args="
Set "Args=!Args:~7,-2!"
#Rem duplicate Args for the purpose of counting doublequotes [destructive].
Set "DQcount=!Args!"
) || (
Echo(%~nx0 requires an NTFS drive system to function as intended.
CMD /C Exit -1073741510
) || Goto:Eof
If Not defined Args Goto:NoArgs
REM substitute doublequotes in Args clone 'DQcount'; count substring in string;
REM assess if count is even; If false "||": Remove doublequotes from string. If true "&&" and if entire
REM arg line is doublequoted, remove outer quotes.
Set Div="is=#", "1/(is<<9)"
Set "{DQ}=0"
Set ^"DQcount=!DQcount:"={DQ}!"
2> nul Set "null=%DQcount:{DQ}=" & Set /A {DQ}+=1& set "null=%"
Set /A !Div:#={DQ} %% 2! 2> nul && Set ^"Args=!Args:"=!" || If [^%Args:~0,1%^%Args:~-1%] == [""] Set "Args=!Args:~1,-1!")
For /f Delims^= %%G in ("!Args!")Do Endlocal & Set "Args=%%G" 2> nul
:NoArgs
=====================================================================
Rem help query assessment
(
Set Args | %SystemRoot%\System32\Findstr.exe /bli "Args=\/? Args=-? Args=Help?" > nul && (
Rem Args value has leading /? -? or help?
If not "%Args:*?=%"=="" (
Rem Args value contains leading /? -? or help? with additional Parameter
Call:%Args:*? =%_Syntax && Goto:Eof || (
Rem quit after Call to Syntax info if valid Parameter; else notify invalid and show valid syntax queries.
Echo(Invalid query: "%Args:*? =%" : Does not Match a valid Help Query:
)
)
Rem show valid syntax queries.
For /F "Tokens=1 Delims=:_" %%G in ('%SystemRoot%\System32\Findstr.exe /R "^:.*_Syntax" "%~f0"') Do Echo(%~nx0 /? %%G
ENDLOCAL & Exit /b 0
)
) 2> nul
Set Args
Goto:Eof
Rem Demo syntax labels
:Demo_Syntax
Echo %~0 help info
Exit /b 0
:Example_Syntax
Echo %~0 help info
Exit /b 0
In batch file, to define a function you put a : sign and then the name of the function
For example:
:func
copy "test.txt" "D:\test"
Then when you want to call the function you use goto:
goto func
Is there a way to do that without a batch file? To define a function from the cmd?
You could make a macro.
set "func=copy "test.txt" "D:\test""
Then to execute it,
%func%
Batch (cmd.exe) doesn't knows functions at all.
With labels you can build a construct that works similar to functions and for returning use exit /b ( or goto :eof),
but in reality this isn't a function.
But labels can only be used inside of batch files!
At the other side you could use macros, these are variables with code.
Also no real functions, but can work like one.
Usage of a macro (in this case to get the string length):
set "var=abcdef"
%strlen% var
And to build the macro you only need to copy this into your command prompt
cmd /v:on
set LF=^
::Above 2 blank lines are required - do not remove
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
:::: StrLen pString
set strLen=#for /L %n in (1 1 2) do #if %n==2 (%\n%
for /F "tokens=1,2 delims=, " %1 in ("!argv!") do #(%\n%
set "str=A^!%~1^!"%\n%
set "len=0"%\n%
for /l %A in (12,-1,0) do #(%\n%
set /a "len|=1<<%A" ^> nul %\n%
for %B in (^^!len^^!) do #if "^!str:~%B,1^!"=="" set /a "len&=~1<<%A" ^> nul%\n%
)%\n%
for %v in (^^!len^^!) do #if "%~2" neq "" (set "%~2=%v") else echo %v%\n%
) %\n%
) ELSE set argv=,
But macros have a tendency to become nasty.
Or you could read DosTips: Executing GOTO/CALL in a cmd.exe, it's a little bit advanced technic
:func is not a function but label in batch file. I.e. point for goto. There is no return like in subroutine/function.
set var=C:\Users\user\Desktop\bla\bla.exe
set var=%var:*\%
echo %var%
this returns Users\user\Desktop\bla\bla.exe - is there any way to make it focus on the last \ and not the first one so that it would just return bla.exe? bear in mind that this will be used on multiple files and folders so i won't always know how many sub-folders there are.
#ECHO OFF
SETLOCAL
set "var=C:\Users\user\Desktop\blah blah\bla.exe"
FOR %%a IN ("%var%") DO (
SET "filename=%%~nxa"
FOR %%b in ("%%~dpa.") DO SET "lastleaf=%%~nxb"
)
ECHO filename is "%filename%"
ECHO lastleaf is "%lastleaf%"
GOTO :EOF
Normally, the next question is about how to obtain the last leaf of the directory-tree. No subroutines required...
Note positioning of quotes to minimise problems with separators. ALso minor directory name-change to exhibit differences.
The following snippet shows one way to do it.
#setlocal enableextensions enabledelayedexpansion
#echo off
:main
set var=C:\Users\user\Desktop\blah blah\yada yada.exe
call :basename result "%var%"
echo %result%
endlocal
goto :eof
:basename
set %1=%~nx2
goto :eof
It basically calls a function basename (named after the UNIX utility), passing the full name and the variable you want to assign the base name to, and you need to make sure you quote it properly lest filenames containing spaces will cause you problems.
The full set of variable modifiers can be seen in the call /? help output.
Alternatively, you can use the same basename functionality in a one-liner for statement:
for /f "delims=" %%I in ("%var%") do set result=%%~nxI
This allows you to get the base name without having to call a function. I tend to prefer the function myself since it's more readable but you could probably alleviate that by just including a comment:
rem Get base name of var into result:
rem eg: var = C:\Users\user\Desktop\blah blah\yada yada.exe
rem result = yada yada.exe
for /f "delims=" %%I in ("%var%") do set result=%%~nxI
I am fighting with little piece of code for last two days.
In this I am not able to set variable in a for loop.
I want to assign a filename to a variable for string manipulation.
echo off
for /f %%a IN ('dir /b *_ah.ttf') DO (
set /a fName=%%~na
echo %fName%
)
When I echo fName variable I get only last filename repeatedly number of times for for loop count.
(I want to pass this variable as an argument to some batch file as follows
ttfhnt --strong-stem-width=D -i %%a %fName:~0,-3%.ttf
but its failing due to above problem)
Can somebody help me please?
When the cmd parser reads a line or a block of lines (the code inside the parenthesis), all variable reads are replaced with the value inside the variable before starting to execute the code. If the execution of the code in the block changes the value of the variable, this value can not be seen from inside the same block, as the read operation on the variable does not exist, as it was replaced with the value in the variable.
This same behaviour is seen in lines where several commands are concatenated with &. The line is fully parsed and then executed. If the first commands change the value of a variable, the later commands can not use this changed value because the read operation replace.
To solve it, you need to enable delayed expansion, and, where needed, change the syntax from %var% to !var!, indicating to the parser that the read operation needs to be delayed until the execution of the command.
And set /A is only used for arithmetic operations
setlocal enabledelayedexpansion
for /f "delims=" %%a IN ('dir /b *_ah.ttf') DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
edited to adapt to comments
While for command is able to execute a command (in the OP code, the dir...), retrieve its output and then iterate over the lines in this output, the original reason for the command is to iterate over a set of files. In this form, the code can be written as
setlocal enabledelayedexpansion
for %%a IN ("*_ah.ttf") DO (
set "fName=%%~na"
echo "!fName!" "!fName:~0,-3!"
)
Now, the for command replaceable parameter will iterate over the indicated set of files. (execute for /? for a list of all the command options).
But as foxidrive points, the problem with delayed expansion are the exclamation signs. Without delayed expansion, they are another normal character, but with delayed expansion they frequently become a problem when a value containig them is assigned/echoed.
A quick test
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
echo normal : %test%
for /f "delims=" %%a in ("!test!") do echo for : %%a
Will show
---------------------
test=this is a test!
---------------------
delayed : this is a test!
normal : this is a test
for : this is a test
Obviously when the value is a file name, this behaviour will make the code find or not the file.
Depending on the case different solutions can be used, but usually it involves the activation / desactivation of the delayed expansion behaviour (beware, the endlocal removes any change in environment variables from the previous setlocal).
#echo off
setlocal enabledelayedexpansion
set "test=this is a test^!"
echo ---------------------
set test
echo ---------------------
echo delayed : !test!
rem Commuted to no delayed expansion
setlocal disabledelayedexpansion
echo normal : %test%
endlocal
rem Cancelled the initial enable delayed expansion
for /f "delims=" %%a in ("!test!") do endlocal & echo for : %%a
rem The last endlocal has removed the changes to the variable
echo no data : [%test%]
In my batch file on Windows XP, I want to use %* to expand to all parameters except the first.Test file (foo.bat):
#echo off
echo %*
shift
echo %*
Call:
C:\> foo a b c d e f
Actual result:
a b c d e f
a b c d e f
Desired result:
a b c d e f
b c d e f
How can I achieve the desired result? Thanks!!
Wouldn't it be wonderful if CMD.EXE worked that way! Unfortunately there is not a good syntax that will do what you want. The best you can do is parse the command line yourself and build a new argument list.
Something like this can work.
#echo off
setlocal
echo %*
shift
set "args="
:parse
if "%~1" neq "" (
set args=%args% %1
shift
goto :parse
)
if defined args set args=%args:~1%
echo(%args%
But the above has problems if an argument contains special characters like ^, &, >, <, | that were escaped instead of quoted.
Argument handling is one of many weak aspects of Windows batch programming. For just about every solution, there exists an exception that causes problems.
That´s easy:
setlocal ENABLEDELAYEDEXPANSION
set "_args=%*"
set "_args=!_args:*%1 =!"
echo/%_args%
endlocal
Same thing with comments:
:: Enable use of ! operator for variables (! works as % after % has been processed)
setlocal ENABLEDELAYEDEXPANSION
set "_args=%*"
:: Remove %1 from %*
set "_args=!_args:*%1 =!"
:: The %_args% must be used here, before 'endlocal', as it is a local variable
echo/%_args%
endlocal
Example:
lets say %* is "1 2 3 4":
setlocal ENABLEDELAYEDEXPANSION
set "_args=%*" --> _args=1 2 3 4
set "_args=!_args:*%1 =!" --> _args=2 3 4
echo/%_args%
endlocal
Remarks:
Does not work if any argument contains the ! or & char
Any extra spaces in between arguments will NOT be removed
%_args% must be used before endlocal, because it is a local variable
If no arguments entered, %_args% returns * =
Does not shift if only 1 argument entered
Don't think there's a simple way to do so. You could try playing with the following workaround instead:
#ECHO OFF
>tmp ECHO(%*
SET /P t=<tmp
SETLOCAL EnableDelayedExpansion
IF DEFINED t SET "t=!t:%1 =!"
ECHO(!t!
Example:
test.bat 1 2 3=4
Output:
2 3=4
Another easy way of doing this is:
set "_args=%*"
set "_args=%_args:* =%"
echo/%_args%
Remarks:
Does not work if first argument (%1) is 'quoted' or "double quoted"
Does not work if any argument contains the & char
Any extra spaces in between arguments will NOT be removed
I had to do this recently and came up with this:
setlocal EnableDelayedExpansion
rem Number of arguments to skip
set skip=1
for %%a in (%*) do (
if not !position! lss !skip! (
echo Argument: '%%a'
) else (
set /a "position=!position!+1"
)
)
endlocal
It uses loop to skip over N first arguments. Can be used to execute some command per argument or build new argument list:
setlocal EnableDelayedExpansion
rem Number of arguments to skip
set skip=1
for %%a in (%*) do (
if not !position! lss !skip! (
set args=!args! %%a
) else (
set /a "position=!position!+1"
)
)
echo %args%
endlocal
Please note that the code above will add leading space to the new arguments. It can be removed like this:
How to remove trailing and leading whitespace for user-provided input in a batch file?
Yet another obnoxious shortcoming of DOS/Windows batch programming...
Not sure if this is actually better than some of the other answers here but thought I'd share something that seems to be working for me. This solution uses FOR loops rather than goto, and is contained in a reusable batch script.
Separate batch script, "shiftn.bat":
#echo off
setlocal EnableDelayedExpansion
set SHIFTN=%1
FOR %%i IN (%*) DO IF !SHIFTN! GEQ 0 ( set /a SHIFTN=!SHIFTN! - 1 ) ELSE ( set SHIFTEDARGS=!SHIFTEDARGS! %%i )
IF "%SHIFTEDARGS%" NEQ "" echo %SHIFTEDARGS:~1%
How to use shiftn.bat in another batch script; in this example getting all arguments following the first (skipped) arg:
FOR /f "usebackq delims=" %%i IN (`call shiftn.bat 1 %*`) DO set SHIFTEDARGS=%%i
Perhaps someone else can make use of some aspects of this solution (or offer suggestions for improvement).
Resume of all and fix all problems:
set Args=%1
:Parse
shift
set First=%1
if not defined First goto :EndParse
set Args=%Args% %First%
goto :Parse
:EndParse
Unsupport spaces between arguments: 1 2 3 4 will be 1 2 3 4