Updating a command line parameter in a batch file - windows

Is it possible to update or replace a command line parameter (like %1) inside of a batch file?
Sample code:
rem test.cmd
#echo off
echo Before %1
IF "%1" == "123" (
set %%1 = "12345678"
)
echo After %1
Desired Result:
C:/>Test 123
Before 123
After 12345678
Actual Result:
C:/>Test 123
Before 123
After 123

No. What you are trying is not possible.
Can be simulated passing original batch parameters to subrutine, or call the same cmd recursively with modified parameters, which again get %1, %2, ... the parameters provided in the call. But this is not what you ask.
rem test.cmd
#echo off
echo Before %1
if "%~1"=="123" (
call :test %1234
) else (
call :test %1
)
goto :EOF
:test
echo After %1

Argument variables are reserved, protected variables, you can't modify the content of one of those variables by yourself.
I suggest you to store the argument in a local variable then you can do all operations you want:
#echo off
Set "FirstArg=%~1"
Echo: Before %FirstArg%
IF "%FirstArg%" EQU "123" (
Set "FirstArg=12345678"
)
Echo: After %FirstArg%
Pause&Exit

Related

Is it possible to pass unprocessed parameters in a CALL command?

Take for instance the following example files a.bat and b.bat:
a.bat
#echo off
echo in a: %*
call b %*
b.bat
#echo off
echo in b: %*
and consider this invocation and output:
c:\>a 1 2 3 %% %%%%
in a: 1 2 3 %% %%%%
in b: 1 2 3 % %%
Is there a way to pass the parameters from the command line to b.bat such that it sees the same parameters that a.bat saw? Is it possible to prevent the system from processing the parameters, i.e. removing the escaping % character?
Because this looks so simple, I assume that I must have missed something, but from what you've posted, this looks like all you should need:
C:\a.cmd
#Echo In %~n0: %*
#Call "b.cmd" %%*
C:\b.cmd
#Echo In %~n0: %*
Example:
C:\>a 1 2 3 %% %%%%
In a: 1 2 3 %% %%%%
In b: 1 2 3 %% %%%%
Yes you can do that, but instead of removing a character, you need to add more escape characters.
But to solve it for any (or at least most) parameter you need to escape all problematic characters &|<>"^
This can handle even parameters like:
a.bat Bob^&Alice say: "Cat & dog"
#echo off
REM *** Fetch all arguments
goto :init :initCallBack
:initCallBack
REM *** Prepare the arguments for a secure CALL
call :prepareArgument allArgs arg
REM *** b.bat will see the same arguments like this file
call b.bat %%allArgs%%
exit /b
:init
::: Get all arguments with minimal interference
::: Only linefeeds and carriage returns can't be fetched
::: Only linefeeds can create a syntax error
::: return arg=All arguments, arg1..arg<n>=A single argument
setlocal EnableDelayedExpansion
set "argCnt="
:getArgs
>"%temp%\getArg.txt" <"%temp%\getArg.txt" (
setlocal DisableExtensions
(set prompt=#)
echo on
if defined argCnt (
for %%a in (%%a) do rem . %1.
) ELSE (
for %%a in (%%a) do rem . %*.
)
echo off
endlocal
set /p "arg%argCnt%="
set /p "arg%argCnt%="
set "arg%argCnt%=!arg%argCnt%:~7,-2!"
if defined arg%argCnt% (
if defined argCnt (
shift /1
)
set /a argCnt+=1
goto :getArgs
) else set /a argCnt-=1
)
del "%temp%\getArg.txt"
goto :initCallBack
:prepareArgument
::: Convert a string, so it can be used as an argument for calling a program
set "var=!%~2!"
if defined var (
set "var=!var:^=^^!"
set "var=!var:"=^^"!"
set "var=!var:&=^&!"
set "var=!var:|=^|!"
set "var=!var:>=^>!"
set "var=!var:<=^<!"
)
set "%~1=!var!"
exit /b

Windows Batch File Expansion Variables

I'm having an issue with a Batch File. Basically, I would like to seek ahead by 1 in a for loop and use that value to reference the parameter at that position and then assign it to a variable which I can then use later, see code sample below.
Example: set myVar=%1+1
Where %1 is param name, and %2 would be param value.
::Batch file sample
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set count=0
set HasParam=0
set ParamValue="null"
set paramValPos=0
for %%i in (%*) do (
set /a count=!count!+1
if /i "%%i"=="MyParam" (
set /a HasParam=1
set /a paramValPos=!count!+1
::The next line is where I Need Help, it's currently incorrect!
set ParamValue=%!paramValPos!
)
)
if %HasParam%==1 (
echo "Parameter Value: %ParamValue%"
)
pause
Example call from command line: prog.bat MyParam=5
Now I know that I can access it at pos 2, like:
::Sample batch file
#echo off
echo "Param Name: %1"
echo "Param Value: %2"
pause
However, this is not what I want, as there are more parameters and some are optional and they can also be passed in, in any order.
Thanks for your assistance.
call set "ParamValue=%%!paramValPos!"
If I understood your request correctly, you want the next parameter after a given one. The Batch code below do that:
#echo off
set "HasParam="
set "ParamValue="
for %%i in (%*) do (
if defined HasParam (
if not defined ParamValue (
set ParamValue=%%i
)
) else if /i "%%i"=="MyParam" (
set HasParam=yes
)
)
if defined HasParam (
echo "Parameter Value: %ParamValue%"
)
pause

Batch script pass multiple parameters to call

I have written a script that contains a function that should loop through a list and return a value given the index of the item in said list. I have a function called: :find that should take 2 arguments: the list and the item position. I am unsure of how to process the multiple parameters in the function. This script runs fine if I replace %LIST% with %MY_LIST% inside the loop and I remove the %MY_LIST% from the argument list tht is passed to call :find, but I really want to know how to pass multiple arguments. I take it that they are just passed to the function as a whole string...
#echo off
setlocal enableDelayedExpansion
cls
:: ----------------------------------------------------------
:: Variable declarations
:: ----------------------------------------------------------
set RETURN=-1
set MY_LIST=("foo" "bar" "baz")
set TARGET_INDEX=1
:: ----------------------------------------------------------
:: Main procedure
:: ----------------------------------------------------------
call :log "Finding item %TARGET_INDEX%..."
call :find %MY_LIST% %TARGET_INDEX%
call :log "The value is: %RETURN%"
goto Exit
:: ----------------------------------------------------------
:: Function declarations
:: ----------------------------------------------------------
:find
call :log "Called `:find` with params: [%*]"
set /a i=0
set LIST=%~1 & shift
for %%a in %LIST% do (
if !i! == %~1 (
set RETURN=%%a
)
set /a i=!i!+1
)
goto:EOF
:printDate
for /f "tokens=2-4 delims=/ " %%a in ('echo %DATE%') do (
set mydate=%%c/%%a/%%b)
for /f "tokens=1-3 delims=/:./ " %%a in ('echo %TIME%') do (
set mytime=%%a:%%b:%%c)
echo|set /p="[%mydate% %mytime%] "
goto:EOF
:log
call :printDate
echo %~1
goto:EOF
:: ----------------------------------------------------------
:: End of script
:: ----------------------------------------------------------
:Exit
Update
My script now works fine; thanks to nephi12. http://pastebin.com/xGdFWmnM
call :find "%MY_LIST%" %TARGET_INDEX%
goto :EOF
:find
echo %~1 %~2
goto :EOF
they are passed the same as args to the script... ;)
Here is another method to do an index lookup on a space delimited list of values. Define the list without enclosing parentheses. Single words don't need to be quoted. Phrases with space, tab, semicolon, or equal must be quoted. Also values with special characters like & and | should be quoted.
Then reverse the order of your :FIND arguments - first the index, followed by the actual list. Use SHIFT in a FOR /L loop to get the desired index value into the first argument.
One advantage of this solution is that it can support any number of values, as long as they fit within the 8191 character per line limit. The nephi12 solution is limited to 9 values.
#echo off
setlocal
set MY_LIST=foo bar baz "Hello world!"
call :find %~1 %MY_LIST%
echo return=%return%
exit /b
:find index list...
for /L %%N in (1 1 %~1) do shift /1
set "return=%~1"
exit /b
Try this, I think it answers your question. Put it in a bat file and then build whatever else you need around it after you see this work. Execute it with a quoted string from a command prompt. YourBatFile "Arg1 Arg2 Arg3 Etc"
#echo off
call :DoSomethingWithEach %~1
goto :eof
:DoSomethingWithEach
for %%a in (%*) do echo.%%a
goto :eof

How to package all my functions in a batch file as a seperate file?

My question is related to this question. I have several bunch of actions that need to be executed from a batch file and I would like to model them as functions and call from a master sequence. From the above question, it is clear that I can do this with the call syntax
call:myDosFunc
My question is that can I place all these functions in a seperate batch file (functions.bat) and somehow 'include' that in the main batch file and call them? Another option would be to utilize the possibility to invoke functions.bat from main.bat with the call syntaxt, but I'm not sure if I can invoke that with a specific function instead of executing the whole batch file.
In short, I'm looking for something similar to the C programming world where my functions reside in a DLL and the main program contains only the high-level logic and calls the functions from the DLL.
I think a routing function in the beginning of a batch file is not that ugly.
You can use something like this at the beginning of a "libbatch.cmd"
call:%*
exit/b
:func1
[do something]
exit/b
:func2
[do something else]
exit/b
Now you can call func2 from another batch with:
call libbatch.cmd func2 params1 param2 ... paramN
this also preserves the errorlevel "thrown" by func2 (exit/b hands over the current errorlevel).
With the second call instead of a goto you ensure that "%1"=="param1" and not func2.
And call will not terminate the batch file if the label does not exist, it simply sets the errorlevel to 1 and puts an error message to 2 (errorout), which could be redirected to nul.
Explanation: %* contains all parameters, so in the example the first line translates to:
call:func2 params1 param2 ... paramN
Here is a simple example of how it might be done.
The function script is called with the name of the function as the first argument, and function arguments as arg2, arg3, ...
Assuming it is called properly, the script shifts the arguments and performs GOTO to the original arg1. Then the function has its arguments starting with the new arg1. This means you can take already written routines and plop them in the utility without having to worry about adjusting the parameter numbers.
The script gives an error if the function argument is not supplied, or if the function argument does not match a valid label within the script.
#echo off
if "%~1" neq "" (
2>nul >nul findstr /rc:"^ *:%~1\>" "%~f0" && (
shift /1
goto %1
) || (
>&2 echo ERROR: routine %~1 not found
)
) else >&2 echo ERROR: missing routine
exit /b
:test1
echo executing :test1
echo arg1 = %1
exit /b
:test2
echo executing :test2
echo arg1 = %1
echo arg2 = %2
exit /b
:test3
echo executing :test3
echo arg1 = %1
echo arg2 = %2
echo arg3 = %3
exit /b
I prefer the GOTO approach that I used above. Another option is to use CALL instead, as Thomas did in his answer.
For a working example of a usefull library of batch functions that uses the CALL technique, see CHARLIB.BAT, a library of routines for processing characters and strings within a batch file. A thread showing development of the library is available here
I wrote CharLib.bat a few years ago. Were I to write it today, I would probably use GOTO instead of CALL.
The problem with introducing a CALL is that it creates issues when passing string literals as parameters. The extra CALL means that a string literal containing % must have the percents doubled an extra time. It also means unquoted poison characters like & and | would need to be escaped an extra time. Those two issues can be addressed by the caller. But the real problem is that each CALL doubles up quoted carets: "^" becomes "^^". There isn't a good way to work around the caret doubling problem.
The problems with the extra CALL don't impact CharLib.bat because string values are passed by reference (variable name) and not as string literals.
The only down side to using GOTO with SHIFT /1 is that you cannot use %0 to get the name of the currently executing routine. I could have used SHIFT without the /1, but then you wouldn't be able to use %~f0 within a routine to get the full path to the executing batch file.
You can use this format - and launch it like this:
call mybat :function4 parameternumber2 parameternumber3
this would be one way of using a library
#echo off
goto %1
:function1
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
:function2
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
:function3
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
:function4
REM code here - recursion and subroutines will complicate the library
REM use random names for any temp files, and check if they are in use - else pick a different random name
goto :eof
You may use an interesting trick that avoids most of the problems that other methods have when they try to make the library functions available to the main program and it is much faster. The only requisites to use this trick are:
The library functions must be called from inside a code block in the main file, and
In that code block no main file functions are called.
The trick consist in "switch the context" of the running Batch file in a way that the library file becomes the running Batch file; this way, all the functions in the library file becomes available to the main code block with no additional processing. Of course, the "context" of the running Batch file must be switched back to the main file before the code block ends.
The way to "switch the context" is renaming the library file with the same name of the running main file (and renaming the main file to another name). For example:
(
rem Switch the context to the library file
ren main.bat orig-main.bat
ren library.bat main.bat
rem From this point on, any library function can be called
. . . .
rem Switch back the context to the original one
ren main.bat library.bat
ren orig-main.bat main.bat
)
EDIT: Working example added
I copied the example below from the screen. Tested in Windows 8, but I also used this method in Win XP:
C:\Users\Antonio\Documents\test
>type main.bat
#echo off
(
rem Switch the context to the library file
ren main.bat orig-main.bat
ren library.bat main.bat
rem From this point on, any library function can be called, for example:
echo I am Main, calling libFunc:
call :libFunc param1
echo Back in Main
rem Switch back the context to the original one
ren main.bat library.bat
ren orig-main.bat main.bat
)
C:\Users\Antonio\Documents\test
>type library.bat
:libFunc
echo I am libFunc function in library.bat file
echo My parameter: %1
exit /B
C:\Users\Antonio\Documents\test
>main
I am Main, calling libFunc:
I am libFunc function in library.bat file
My parameter: param1
Back in Main
I'm not sure of the context of the original question, but this might be a case where switching to something like WSH with VBScript or WPS, or any other console-capable scripting other than batch files. I will answer the original question, but first.. a little background and understanding..
The command line/console mode of DOS and Windows is usually either COMMAND.COM or CMD.EXE, which isn't well-geared towards scripting/programming logic. Rather, they're geared towards executing commands and programs, and batch files were added to commonly used sequences of commands to wrapped up in a single typed command. For example, you may have an old DOS game you play that needs the following commands every time, so it's packaged as a batch file:
#EHO OFF
#REM Load the VESA driver fix..
VESAFIX.EXE
#REM Load the joystick driver..
JOYSTICK.COM
#REM Now run the game
RUNGAME.EXE
Many people tend to view the entire batch file as one atomic unit--But it's not. The command interpreter (COMMAND.COM or CMD.EXE) will merely act like you manually typed those lines, one by one, each time you run the batch file. It really has no solid concept of lexica and scoping like a regular programming/scripting language would--that is, it doesn't maintain much extra meta-data like a call stack, et cetera. What little it does maintain is more added like it's an afterthought rather than built-in to batch file from the beginning.
Once you shift the gears in your thinking, however, you can often overcome this limitation using various tricks and techniques to emulate more powerful scripting/programming languages; But you still have to remember that batch files are still going to be limited, regardless.
Anyhow, one technique of using a batch file library is to create a batch file where the first parameter is used to indicate which function is being called:
CALL BATLIB.BAT FunctionName Parameter1 Parameter2 ...
This works well enough when the library is written with this in mind, so it'll know to skip the first argument, et cetera.
Using more modern version of CMD.EXE in Windows' systems allows the use of ":labels" in the CALL syntax, which can be useful if you want to limit the parameter scope (which allows you to use %* for "all arguments", for example), like this:
CALL :LABEL Parameter1 Paramater2 ...
(from within the same batch file or ...)
CALL BATLIB.BAT :LABEL Parameter1 Parameter2 ...
A few notes about that, though.. In the first form, the :LABEL must be already within the current batch file. It will create a new "batch context" within CMD.EXE where %*, %1, %2, et cetera are matched to the parameters. But you'll also have to provide some kind of return/exit logic to return/exit from that context back to the calling context.
In the second form, CMD.EXE does not really recognize that you are passing it a label, so your batch file library will have to expect it and handle it:
#ECHO OFF
CALL %*
This works because the command interpreter replaces the %* before it even attempts to parse the CALL command, so after variable expansion, the CALL command would see the :LABEL as if it were hard-coded. This also creates a situation where CMD.EXE creates yet another batch context, so you'll have to make sure to return/exit from that context twice: Once for the current library context, again to get back to the original CALL.
There are still other ways to do a batch file library, mixing and matching the above techniques, or using even more complex logic, using GOTO's, et cetera. This is actually such a complex topic that there are entire sections of books written on the topic, much more than I want to type in a simple answer here!
And so far, I've mostly ignored other issues you will encounter: What if the CALL label does not exist? How will it be handled? What about environment variable expansion? when does it happen? How can you prevent it from happening too soon? What about using special DOS characters in the arguments/parameters? For example, how does the interpreter see a line like: CALL :ProcessPath %PATH%? (The answer to that is that CMD.EXE replaces the entire %PATH% before it even processes the CALL command. This can create issues if your path has spaces in it which can trip up how CALL processes the entire thing, as many Windows' %PATH% variables do.. C:\Program Files.. for example..)
As you can see, things are getting complicated and messy very quickly.. And you have to stop thinking like a programmer and start thinking like COMMAND.COM/CMD.EXE, which pretty much only sees one single line at a time, not the whole batch file as an atomic unit. In fact, here's an example to help you really grasp the way it works..
Create a folder, C:\testing, and put the following batch, file called "oops.bat", in it:
#ECHO OFF
ECHO Look mom, no brain!
PAUSE
ECHO Bye mom!
Now open a console window and run it, but let it sit there at the PAUSE:
C:\testing>oops.bat
Look mom, no brain!
Press any key to continue . . .
While it's sitting at the PAUSE, open oops.bat in your text editor and change it to:
#ECHO OFF
ECHO Look mom, no brain!?
ECHO Oops!
PAUSE
ECHO Bye mom!
Save it, then switch back to your console window and press any key to continue running the batch file:
'ops!' is not recognized as an internal or external command,
operable program or batch file.
Press any key to continue . . .
Bye mom!
c:\testing>
Whoa.. see that error there? That happened because we edited the batch file while it was still being run by CMD.EXE, but our edits changed where in the batch file CMD.COM thought it was. Internally, CMD.EXE maintains a file pointer indicating the start of the next character to process, which in this case would have been the byte right after the line with the PAUSE (and the CRLF) on it. But when we edited it, it changed the location of the next command in the batch file, but CMD.EXE's pointer was still in the same place. In this case, it was pointing to the byte position right in the middle of the "ECHO Oops!" line, so it tried to process "ops!" as a command after the PAUSE.
I hope this makes it clear that COMMAND.COM/CMD.EXE will always see your batch files as a stream of bytes, not as a logical block, subroutines, et cetera, like a script language or compiler would. This is why batch file libraries are so limited. It makes it impossible to "import" a library in to a currently running batch file.
Oh, and I just had another thought.. In modern Windows' CMD.EXE, you can always create a batch file which creates a temporary batch file on the fly, then calls it:
#ECHO OFF
SET TEMPBAT=%TEMP%\TMP%RANDOM:~0,1%%RANDOM:~0,1%%RANDOM:~0,1%%RANDOM:~0,1%.BAT
ECHO #ECHO OFF > %TEMPBAT%
ECHO ECHO Hi Mom! I'm %TEMPBAT%! >> %TEMPBAT%
ECHO Hello, world, I'm %~dpnx0!
CALL %TEMPBAT%
DEL %TEMPBAT%
This effectively creates a temporary batch file in your temporary directory, named TMP####.BAT (where the #'s are replaced by random numbers; The %RANDOM:~0,1% means take the first digit of the number returned by %RANDOM%--we only wanted one single digit here, not the full number that RANDOM returns..), then ECHO's "Hello, World," followed by it's own full name (the %~dpnx0 part), CALLs the temporary batch file, which in turn ECHO's "Hi Mom!" followed by it's own [random] name, then returns to the original batch file so it can do whatever cleanup is needs, such as deleting the temporary batch file in this case.
Anyhow, as you can see by the length of this post, this topic really is not a simple one. There's dozens or more web pages out on the web with tons of batch file tips, tricks, et cetera, many of which go in to depth about how to work with them, create batch file libraries, what to watch out for, how to pass arguments by reference vs. by value, how to manage when and where variables get expanded, and so on.
Do a quick Google search for "BATCH FILE PROGRAMMING" to find many of them, and you can also check out Wiki and WikiBooks, SS64.com, robvanderwoude.com and even DMOZ's directory with more resources.
Good luck!
Here's a cmd batch script, which imports files or files in folders (recursivelly) into the main script:
#echo off
REM IMPORT - a .cmd utility for importing subroutines into the main script
REM !!! IN ORDER TO FUNCTION CORRECTLY: !!!
REM !!! IMPORT MUST BE CALLED INSIDE A DISABLED DELAYED EXPANSION BLOCK/ENVIRONMENT (DEFAULT) !!!
rem \\// Define import file mask here:
rem If mask is not defined outside "import.cmd":
if not defined mask (
set "mask=*.cmd; *.bat"
)
rem //\\ Define import file mask here:
rem Detect if script was started from command line:
call :DetectCommandLine _not_started_from_command_line
if "%~1" == "/install" (
set "import_path=%~dp0"
call :EscapePathString import_path import_path_escaped
)
if not "%~1" == "" (
if /i not "%~1" == "end" (
if "%~1" == "/?" (
call :DisplayHelp
) else (
if "%~1" == "/install" (
echo Installing
set "_first_time="
rem This should get into the Autorun registry key: path %path%;"...\import.cmd"
rem If you want, other commands can be added to the left or to the right of this command, unified as a block with a "&" command operator
REG ADD "HKCU\Software\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d "path %%path%%;"""%import_path_escaped%""||(
echo ERROR: Cannot install import: cannot write to the registry^!
echo You can try to manually add the "import.cmd" path in the "PATH" variable or use pushd ^(see help^).
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
echo.
echo Done. The install directory was set to:
echo "%import_path%"
echo and was added to the PATH environment variable. You can add other desired programs into this directory.
echo.
echo Please note that the console needs to be closed and reopened in order for the changes to take effect^!
echo.
) else (
if not defined _first_time (
set _first_time=defined
set /a count=0
if "%_DISPLAY_WARNING%" == "true" (
echo.
echo WARNING: CMD_LIBRARY was reset to "", because previously it gave an error^!
echo.
)
echo Loading list to import...
)
REM build import files list
set /a count+=1
call set "_import_list_%%count%%=%%~1"
)
)
)
) else (
call :DisplayHelp
)
if /i "%~1" == "end" (
set "_first_time="
echo Done.
echo Analyzing...
rem set "_main_program=%~dpnx2"
if not exist "%~dpnx2" (
echo ERROR: Second parameter, after "import end", must be a valid file path - see help^!>>&2
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
)
if /i "%~1" == "end" (
set "_main_batch_script=%~dpnx2"
rem \\// Define output filename here:
rem set "_output_filename=tmp0001_%~n2.cmd"
set "_output_filename=tmp0001.cmd"
rem //\\ Define output filename here:
)
if /i "%~1" == "end" (
rem Check all paths not to be UNC:
setlocal EnableDelayedExpansion
set "_error=false"
call :TestIfPathIsUNC _main_batch_script _result
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: Second parameter, after "import end", must not be a UNC path^^^! Currently it is: "!_main_batch_script!">>&2
)
set "_CMD_LIBRARY_error=false"
call :TestIfPathIsUNC CMD_LIBRARY _result
if "!_result!" == "true" (
set "_error=true"
set "_CMD_LIBRARY_error=true"
echo.
echo ERROR: UNC paths are not allowed: CMD_LIBRARY variable must not contain a UNC path^^^! Currently, it is set to: "!CMD_LIBRARY!".>>&2
)
for /l %%i in (1,1,!count!) do (
call :TestIfPathIsUNC _import_list_%%i _result
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: The import path: "!_import_list_%%i!" is a UNC path^^^!>>&2
)
)
if "!_error!" == "true" (
echo.
echo Errors were ecountered^^^!
if "!_CMD_LIBRARY_error!" == "true" (
endlocal
set "_CMD_LIBRARY_error=true"
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
)
if /i "%~1" == "end" (
rem Check all paths not to contain "*" and "?" wildcards:
set "_asterisk=*"
set "_qm=?"
setlocal EnableDelayedExpansion
set "_error=false"
call :TestIfStringContains _main_batch_script _asterisk _result1
call :TestIfStringContains _main_batch_script _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: Second parameter, after "import end", must not contain "*" or "?" wildcards^^^! Currently it is: "!_main_batch_script!". Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
set "_CMD_LIBRARY_error=false"
call :TestIfStringContains CMD_LIBRARY _asterisk _result1
call :TestIfStringContains CMD_LIBRARY _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
set "_error=true"
set "_CMD_LIBRARY_error=true"
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: CMD_LIBRARY variable must not contain "*" or "?" wildcards^^^! Currently, it is set to: "!CMD_LIBRARY!". Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
for /l %%i in (1,1,!count!) do (
call :TestIfStringContains _import_list_%%i _asterisk _result1
call :TestIfStringContains _import_list_%%i _qm _result2
if "!_result1!" == "true" (
set "_error=true"
)
if "!_result2!" == "true" (
set "_error=true"
)
if "!_error!" == "true" (
set "_error=true"
echo.
echo ERROR: The use of "*" or "?" wildcards is not supported by import: The import path: "!_import_list_%%i!" must not contain "*" or "?" wildcards^^^! Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat">>&2
)
)
if "!_error!" == "true" (
echo.
echo Errors were ecountered^^^!
if "!_CMD_LIBRARY_error!" == "true" (
endlocal
set "_CMD_LIBRARY_error=true"
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
)
if /i "%~1" == "end" (
pushd "%~dp2"
call set "_output_dir=%%CD%%"
popd
)
if /i "%~1" == "end" (
if not defined CMD_LIBRARY (
set CMD_LIBRARY_CASE=IMPORT.CMD
set "BASE=%~dpnx0\.."
pushd "%~dpnx0"\..\
REM \\// Define CMD LIBRARY here ("." is relative to "import.cmd" parent directory):
REM if CMD_LIBRARY is not defined outside import.cmd, "." (used here) is related to import.cmd parent directory:
set "CMD_LIBRARY=."
REM //\\ Define CMD LIBRARY here ("." is relative to "import.cmd" parent directory):
) else (
set CMD_LIBRARY_CASE=MAIN.CMD
set "BASE=%~dpnx2\.."
REM if CMD_LIBRARY is defined outside the "import.cmd" script, "." (used in CMD_LIBRARY) is related to "main program" parent directory
pushd "%~dpnx2"\..
)
)
if /i "%~1" == "end" (
call :DeQuoteOnce CMD_LIBRARY CMD_LIBRARY
call set "CMD_LIBRARY_ORIGINAL=%%CMD_LIBRARY%%"
call :TestIfPathIsUNC CMD_LIBRARY_ORIGINAL _result
setlocal EnableDelayedExpansion
if "!_result!" == "true" (
set "_error=true"
echo.
echo ERROR: UNC paths are not allowed: CMD_LIBRARY variable must not contain a UNC path^^^! Currently, it is set to: "!CMD_LIBRARY_ORIGINAL!".>>&2
echo.
echo Errors were ecountered^^^!
endlocal
set "_CMD_LIBRARY_error=true"
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
set "_CMD_LIBRARY_error=false"
)
call pushd "%%CMD_LIBRARY%%" >nul 2>nul&&(
call set "CMD_LIBRARY=%%CD%%"
)||(
call echo ERROR: Could not access directory CMD_LIBRARY=^"%%CMD_LIBRARY%%^"^!>>&2
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
popd
exit /b 1
)
)
if /i "%~1" == "end" (
setlocal EnableDelayedExpansion
set _error=false
pushd "!BASE!"
echo.
if "!CMD_LIBRARY_CASE!" == "IMPORT.CMD" (
echo CMD_LIBRARY was defined as: "!CMD_LIBRARY_ORIGINAL!" in the file "import.cmd" and was expanded to: "!CMD_LIBRARY!"
) else (
if "!CMD_LIBRARY_CASE!" == "MAIN.CMD" (
echo CMD_LIBRARY was defined as: "!CMD_LIBRARY_ORIGINAL!" outside "import.cmd" file "%~nx2" and was expanded to: "!CMD_LIBRARY!"
)
)
for /l %%i in (1,1,!count!) do (
if not exist "!_import_list_%%i!" (
if not exist "!CMD_LIBRARY!\!_import_list_%%i!" (
rem if first time:
if not "!_error!" == "true" (
echo.
echo Directory of "!CMD_LIBRARY!":
)
echo.
echo ERROR: element "!_import_list_%%i!" does not exist or is not accessible as a standalone file/dir or as a file/dir in the directory contained by "CMD_LIBRARY" variable^^^!>>&2
set _error=true
)
)
)
popd
if "!_error!" == "true" (
endlocal
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
endlocal
)
echo OK
echo.
)
set "_error=false"
if /i "%~1" == "end" (
echo Output file is: "%_output_dir%\%_output_filename%"
echo.
echo Importing...
echo.
(
type nul>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not write to file: "%_output_dir%\%_output_filename%"^!>>&2
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
)
echo Importing main script "%_main_batch_script%"
(
echo #set _import=defined
echo #REM Timestamp %date% %time%
echo.
)>>"%_output_dir%\%_output_filename%"
(
(
type "%_main_batch_script%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(echo ERROR: Could not read file^!&set "_error=true">>&2)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
echo.
echo Directory of "%CMD_LIBRARY%":
if not "%CMD_LIBRARY_CASE%" == "MAIN.CMD" (
pushd "%BASE%"
)
if not defined mask (
rem If mask is not defined, import all file types:
set "mask=*"
)
for /l %%i in (1,1,%count%) do (
call set "_import_list_i=%%_import_list_%%i%%"
call :ProcedureImportCurrentFile
)
if not "%CMD_LIBRARY_CASE%" == "MAIN.CMD" (
popd
)
)
if "%~1" == "end" (
if "%_error%" == "true" (
echo.
echo Errors were ecountered^!
rem Clean up
call :CleanUp
if not "%_not_started_from_command_line%" == "0" (
call :PressAnyKey Press any key to exit...
echo.
)
exit /b 1
) else (
echo Done^!
)
call popd
popd
rem Clean up
call :CleanUp
rem Detect if script was started from command line:
call :DetectCommandLine _not_started_from_command_line
)
if "%~1" == "end" (
if "%_not_started_from_command_line%" == "0" (
set "_import="
) else (
echo.
echo Starting program...
echo.
rem Start "resulting" program:
"%_output_dir%\%_output_filename%"
)
)
goto :eof
REM \\\/// Next subroutines use jeb's syntax for working with delayed expansion: \\\///
:CleanUp
(
setlocal EnableDelayedExpansion
)
(
endlocal
if "%_CMD_LIBRARY_error%" == "true" (
set "CMD_LIBRARY="
set "_DISPLAY_WARNING=true"
) else (
set "_DISPLAY_WARNING=false"
)
set "_first_time="
for /l %%i in (1,1,%count%) do (
set "_import_list_%%i="
)
rem optional:
set "count="
set "import_path="
rem set "_output_dir="
set "_error="
set "_main_batch_script="
rem set "_output_filename="
rem set "_import="
set "mask="
exit /b
)
:GetStrLen - by jeb - adaptation
(
setlocal EnableDelayedExpansion
set "s=!%~1!#"
set "len=0"
for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
if "!s:~%%P,1!" NEQ "" (
set /a "len+=%%P"
set "s=!s:~%%P!"
)
)
)
(
endlocal
set "%~2=%len%"
exit /b
)
:EscapePathString
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
call :GetStrLen string string_len
set /a string_len-=1
for /l %%i in (0,1,!string_len!) do (
rem escape "^", "(", ")", "!", "&"
if "!string:~%%i,1!" == "^" (
set "result=!result!^^^^"
) else (
if "!string:~%%i,1!" == "(" (
set "result=!result!^^^("
) else (
if "!string:~%%i,1!" == ")" (
set "result=!result!^^^)"
) else (
if "!string:~%%i,1!" == "^!" (
set "result=!result!^^^!"
) else (
if "!string:~%%i,1!" == "&" (
set "result=!result!^^^&"
) else (
if "!string:~%%i,1!" == "%%" (
set "result=!result!%%"
) else (
set "result=!result!!string:~%%i,1!"
)
)
)
)
)
)
)
)
(
endlocal
set "%~2=%result%"
exit /b
)
:PressAnyKey
set /p=%*<nul
pause>nul
goto :eof
:DeQuoteOnce
(
setlocal EnableDelayedExpansion
set "string=!%~1!"
if "!string!" == """" (
endlocal
set "%~2=%string%"
exit /b
)
rem In order to work with " we replace it with a special character like < > | that is not allowed in file paths:
set "string=!string:"=^<!"
if "!string:~0,1!" == "<" (
if "!string:~-1,1!" == "<" (
set "string=!string:~1,-1!"
)
)
rem restore " in string (replace < with "):
set "string=!string:<="!"
)
(
endlocal
set "%~2=%string%"
exit /b
)
:TestIfPathIsUNC
(
setlocal EnableDelayedExpansion
set "_current_path=!%~1!"
set "_is_unc_path=true"
if defined _current_path (
if "!_current_path:\\=!" == "!_current_path!" (
set "_is_unc_path=false"
)
) else (
set "_is_unc_path=false"
)
)
(
endlocal
set "%~2=%_is_unc_path%"
exit /b
)
:TestIfStringContains
(
setlocal EnableDelayedExpansion
echo "!%~1!"|find "!%~2!">nul 2>nul
set "_error_code=!ERRORLEVEL!"
)
(
endlocal
if "%_error_code%" == "0" (
set "%~3=true"
) else (
set "%~3=false"
)
exit /b
)
REM ///\\\ The subroutines above use jeb's syntax for working with delayed expansion: ///\\\
:DetectCommandLine
setlocal
rem Windows: XP, 7
for /f "tokens=*" %%c in ('echo "%CMDCMDLINE%"^|find "cmd /c """ /c') do (
set "_not_started_from_command_line=%%~c"
)
if "%_not_started_from_command_line%" == "0" (
rem Windows: 10
for /f "tokens=*" %%c in ('echo "%CMDCMDLINE%"^|find "cmd.exe /c """ /c') do (
set "_not_started_from_command_line=%%~c"
)
)
endlocal & (
set "%~1=%_not_started_from_command_line%"
)
goto :eof
:ProcedureImportCurrentFile
setlocal
set "cc="
if not exist "%_import_list_i%" (
set "_not_a_dir=false"
pushd "%CMD_LIBRARY%\%_import_list_i%\" 1>nul 2>&1||set "_not_a_dir=true"
call :GetStrLen CD _CD_len
)
if "%_not_a_dir%" == "false" (
setlocal EnableDelayedExpansion
if not "!CD:~-1,1!" == "\" (
endlocal
set /a _CD_len+=1
) else (
endlocal
)
popd
)
if not exist "%_import_list_i%" (
if "%_not_a_dir%" == "true" (
echo Importing file "%CMD_LIBRARY%\%_import_list_i%"
(
type "%CMD_LIBRARY%\%_import_list_i%">>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
if not "%%i" == "%count%" (
echo.
echo.
) else (
echo.
)
)>>"%_output_dir%\%_output_filename%"
) else (
echo Importing dir "%_import_list_i%"
rem
pushd "%CMD_LIBRARY%\%_import_list_i%\"
set /a cc=0
for /r %%f in (%mask%); do (
set "_current_file=%%~dpnxf"
call set "r=%%_current_file:~%_CD_len%%%"
call echo Importing subfile "%%_import_list_i%%\%%r%%"
(
(
call type "%%_current_file%%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
set /a cc+=1
)
popd
)
) else (
set "_not_a_dir=false"
pushd "%_import_list_i%\" 1>nul 2>&1||set "_not_a_dir=true"
call :GetStrLen CD _CD_len
)
if "%_not_a_dir%" == "false" (
setlocal EnableDelayedExpansion
if not "!CD:~-1,1!" == "\" (
endlocal
set /a _CD_len+=1
) else (
endlocal
)
popd
)
if exist "%_import_list_i%" (
if "%_not_a_dir%" == "true" (
echo Importing file "%_import_list_i%"
(
type "%_import_list_i%">>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
if not "%%i" == "%count%" (
echo.
echo.
) else (
echo.
)
)>>"%_output_dir%\%_output_filename%"
) else (
rem
echo Importing dir "%_import_list_i%"
pushd "%_import_list_i%\"
set /a cc=0
for /r %%f in (%mask%); do (
set "_current_file=%%~dpnxf"
call set "r=%%_current_file:~%_CD_len%%%"
call echo Importing subfile "%%_import_list_i%%\%%r%%"
(
(
call type "%%_current_file%%"
)>>"%_output_dir%\%_output_filename%"
) 2>nul||(
echo ERROR: Could not read file^!>>&2
set "_error=true"
)
(
echo.
echo.
)>>"%_output_dir%\%_output_filename%"
set /a cc+=1
)
popd
)
)
if "%cc%" == "0" (
echo No match^!
)
endlocal & (
set "_error=%_error%"
)
goto :eof
:DisplayHelp
echo IMPORT - a .cmd utility for importing subroutines into the main script
echo.
echo NOTES: 1. This utility assumes that command extensions are enabled (default) and that delayed expansion can be enabled;
echo ALSO IMPORT MUST BE CALLED INSIDE A DISABLED DELAYED EXPANSION BLOCK/ENVIRONMENT (DEFAULT);
echo These are necessary in order for it to function correctly.
echo 2. The use of UNC paths is not supported by import. As a workarround, you can mount a UNC path to a temporary drive using "pushd".
echo The use of "*" or "?" wildcards is not supported by import. Instead, you can set the mask with a set of name and extension masks sepparated by semicolon, like: set mask="*.cmd; *.bat"
echo When the "mask" variable is set, only the filenames having the extensions contained by it are matched at import.
echo.
echo Description:
echo import organizes your batch programs on common libraries of subroutines, that you can use in the future for other programs that you build; it also makes code editing and debugging easier.
echo.
echo Usage [1]:
echo import [flags]
echo.
echo [flags] can be:
echo /install - installs import into the registry, in the Command Processor AutoRun registry key ^(adds the current location of import into the PATH variable^).
echo /? - displays help ^(how to use import^)
echo.
echo Usage [2]:
echo What it does:
echo Concatenates ^(appends^) files content containing subroutines to the main program content using the following SYNTAX:
echo REM \\//Place this in the upper part of your script ^(main program)^ \\//:
echo.
echo #echo off
echo.
echo if not defined _import ^(
echo rem OPTIONAL ^(before the "import" calls^):
echo set "CMD_LIBRARY=^<library_directory_path^>"
echo.
echo import "[FILE_PATH1]filename1" / "DIR_PATH1"
echo ...
echo import "[FILE_PATHn]filenamen" / "DIR_PATHn"
echo import end "%%~0"
echo ^)
echo.
echo REM //\\Place this in the upper part of your script ^(main program)^ //\\:
echo.
echo "filename1" .. "filenamen" represent the filenames that contain the subroutines that the user wants to import in the current ^(main^) program. The paths of these files are relative to the directory contained in the CMD_LIBRARY variable.
echo.
echo "FILE_PATH1" .. "FILE_PATHn" represent the paths of these files.
echo.
echo "DIR_PATH1" .. "DIR_PATHn" represent directories paths in which to recursivelly search and import all the files of the type defined in the variable "mask"
echo.
echo CMD_LIBRARY is a variable that contains the directory path where your library of files ^(containing subroutines^) is found.
echo.
echo We denote the script that calls "import" as "the main script".
echo.
echo By default, if not modified in outside the import.cmd script, in the import.cmd script - CMD_LIBRARY is set to "." directory and is relative to the "import.cmd" parent directory.
echo If CMD_LIBRARY directory is modified outside the import.cmd script, CMD_LIBRARY is relative to the main script parent directory.
echo.
echo Note that only the last value of "CMD_LIBRARY" encountered before `import end "%%~0"` is taken into consideration.
echo.
echo import end "%%~0" - marks the ending of importing files and the start of building of the new batch file ^(named by default tmp0001.cmd, and located in the directory in which the main script resides^).
echo.
echo "%%~0" represents the full path of the main script.
goto :eof
To use it:
save it as import.cmd
call it with the /install flag in order to install it (does not require admin)
add a header like this at the begining of your main script that calls subroutines that are stored in other files - files that are going to be imported:
if not defined _import (
rem OPTIONAL (before the "import" calls):
set "CMD_LIBRARY=<library_directory_path>"
import "[FILE_PATH1]filename1" / "DIR_PATH1"
...
import "[FILE_PATHn]filenamen" / "DIR_PATHn"
import end "%~0"
)
To find out how to use it, simply call it with the /? flag.

how to get batch file parameters from Nth position on?

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

Resources