Take multiple arguments in batch file - batch-file

I have a batch file test.bat
I understand we can give it multiple arguments and take those values using %1, %2, and so on. But I do not know how many arguments will be given. I thought of constructing an array and for loop to decide. But the point I am failing is constructing the for loop.
what I did is:
set count=0
// for loop until we have arguments left
set list[%count%]=%var%
set /A count=count+1
I know little bit of for loop in batch files, but I do not quite understand how to use in this scenario. Number of arguments are unknown. Any direction would be appreciated?

%* contains all arguments.
you could
for %%a in (%*) do echo %%a
or use shift, which advances %1 to the next argument and so on.
:loop
echo %1
shift
if "%1" neq "" goto :loop

Related

Checking parameters for information

I have a Script that reads other .bat Files and counts the Commentlines. I pass the .bat file that i want to test as a parameter.
For example, this is how i start my script.
C:\User\User\Desktop>CommentReader.bat test.bat
Right now test.bat needs to be in the same folder with the Skript.
I want the option that I can pass multiple .bat files that need testing and the option that I can pass the path too. for example:
C:\User\User\Desktop>CommentReader.bat D:\testfolder\test1.bat E:\test2.bat test3.bat
I also want to pass commands for example /l so the script also reads empty lines.
C:\User\User\Desktop>CommentReader.bat /l D:\testfolder\test1.bat E:\test2.bat test3.bat
I know how I can code that it should also read empty lines but
what is the best way to go through my parameters and check the information?
My Idea was something like this:
FOR /f tokens=1,2,3,4,5,6,7,8,9, delims= " %%a in (%*) DO (
)
But maybe there is a better way?
As Stephan stated in the comments section, SHIFT is what you're looking for. This script will loop through all parameters and echo them out. Just replace the ECHO command with your actual code.
#ECHO OFF
:LOOP
IF NOT "%1"=="" (
ECHO %1
SHIFT
GOTO LOOP
)
Your first parameter is always %1, the second one is %2 and so on. The SHIFT command will "forget" the original %1 and decrease all indices by 1, so %2 will become %1, %3 will become %2 and so on. As soon as you reach the last argument SHIFT will turn %1 into an empty string so that you beak out of the loop.

How to use %% inside %% in batch file? I know it's a little confused, click in

I know the question is a little confusing.
This is my script. (easy version)
I've set up those variables
p1 = 1
p2 = 0
p3 = 1
and it goes on and on.
I want to show a list of all of those P values. Then,
I setup a variable "i"
set /a i = 0
:loop
set /a i = %i% + 1
echo P%i% = %p%i%%
goto loop
but then I realized that %p%i%% is not working...
It won't go to %p1% and show me "1",
it just shows me %p1%. and in my full script, it shows me something crazy... SO CAN ANYONE HELP ME OUT HERE?? THANKS A LOT!!
Two simple ways.
The first is to use delayed expansion.
First expand i with %i% and later the expression !p1!
Or use CALL to parse a line a second time.
The line call echo P%i% = %%p%i%%% after the first parse looks like
call echo P1 = %p1%
The second parse will expand %p1% to the content.
setlocal EnableDelayedExpansion
set p1=1
set p2=0
set p3=1
set /a i = 0
:loop
set /a i = %i% + 1
echo P%i% = !p%i%!
call echo P%i% = %%p%i%%%
if %i% == 3 exit /b
goto loop
Or another solution using a for loop
for /L %%i in (1 1 3) do (
echo P%%i = !p%%i!
)
I offer two more solutions in addition to the solutions in answer of jeb not using delayed expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "MyP1=1"
set "MyP2=0"
set "MyP3=5"
set "Count=0"
for /F "tokens=1* delims==" %%I in ('set MyP 2^>nul') do echo Variable %%I has value %%J & set /A Count+=1
echo Variables count is: %Count%
echo/
set "Index=1"
:Loop
if not defined MyP%Index% goto ExitLoop
set /A Value=MyP%Index%
echo Variable MyP%Index% has value %Value%
set /A Index+=1
goto Loop
:ExitLoop
set /A Index-=1
echo Variables count is: %Count%
endlocal
echo/
pause
The environment variables with undetermined number start all with the string MyP. The first approach runs set MyP in a separate command process in background which outputs all environment variables starting with MyP with their values sorted alphabetically, i.e. the output is for this batch code:
MyP1=1
MyP2=0
MyP3=5
This output is processed line by line by FOR which splits each line up into two substrings because of delims== with first substring being name of the environment variable assigned to loop variable I and everything after first equal sign in the line assigned to next loop variable J according to ASCII table which is the value of the environment variable because of tokens=1*.
The names of the environment variables and their values are output with counting the number of variables starting with MyP which is also output after the FOR loop.
The number of the environment variables must not be consecutive increasing for the first solution.
The second solution uses a loop without FOR. This loop requires that the environment variables have an incrementing number in their names. The loop exits if there is no more environment variable with current index number in name.
The value of the current environment variable is assigned to environment variable Value using an arithmetic expression because in this case a string like MyP1 in the arithmetic expression is interpreted as name of an environment variable whose value should be converted to an integer for evaluation of the expression. The integer result of the arithmetic expression is converted back to a string and assigned to the environment variable left to the equal sign.
The answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? might be helpful to understand the environment variables usage techniques as used here.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
goto /?
if /?
pause /?
setlocal /?
See also Single line with multiple commands using Windows batch file for an explanation of & operator used on FOR command line to avoid the need of a command block.

Is there such a thing as a batch code optimizer?

So you know , when people write code they do it neat, best look and most understandable. but Is there an optimizer for batch? Could someone make one?
example-
it takes existing variables and replaces them with the shortest possible variants
set whatever=whatever
echo question
set /p answer=:
if %answer%=%whatever% whatever
and turns it into
set a=whatever
echo question
set /p b=:
if %b%=%a% whatever
so it basically shortens the variables , flags (or tags or whatever like :top) and does other things I cannot thing of to basically optimize everything.
There are certain constructs in Batch programs that slow down the execution. The sole construct that have a major impact for this point is assemble a loop via a GOTO instead of any type of FOR command. If a large program with many GOTO's is rewritten with FOR commands, an important time saving is expected. Another aspect that affect this point is the number of commands/lines a program have, that is, a program that get the same result than another one with less lines, will run faster. The way to achieve the same things with less commands is making good use of Batch file capabilities.
For example, this code:
set /A b=a+8
set /A c=b*2
set /A d=c+e
... run slower than this one:
set /A b=a+8, c=b*2, d=c+e
This code:
command-that-return-errorlevel
if %errorlevel% equ 1 goto label-1
if %errorlevel% equ 2 goto label-2
if %errorlevel% equ 3 goto label-3
... run slower than this one:
command-that-return-errorlevel
for %%e in (1 2 3) do if %errorlevel% equ %%e goto label-%%e
... and previous one run slower than this one:
command-that-return-errorlevel
goto label-%errorlevel%
Shorten the variable names have a very little impact in the execution speed.
This way, the best option is to write Batch files using these techniques from the very beginning. There is no easy way to develop a program that read a Batch file and perform the previous changes, that is, replace GOTO's with FOR and "compress" several lines in less ones.
The program below is just for fun!
#echo off
setlocal EnableDelayedExpansion
rem Get a list of current variable names
(for /F "delims==" %%v in ('set') do (
echo %%v
)) > varnames.txt
rem Call the Batch file, so it creates its variables
call "%~1"
rem Create an array with new variable names
set "_numVars="
set "_nextVar="
< varnames.txt (for /F "delims==" %%v in ('set') do (
if not defined _nextVar set /P _nextVar=
if "%%v" neq "!_nextVar!" (
set /A _numVars+=1
set "var[!_numVars!]=%%v"
) else (
set "_nextVar="
)
))
del varnames.txt
rem Rename the variables in a new file
setlocal DisableDelayedExpansion
(for /F "delims=" %%a in ('findstr /N "^" "%~1"') do (
set "line=%%a"
setlocal EnableDelayedExpansion
set "line=!line:*:=!"
if defined line (
for /L %%i in (1,1,%_numVars%) do (
for /F "delims=" %%v in ("!var[%%i]!") do set "line=!line:%%v=v%%i!"
)
)
echo(!line!
endlocal
)) > "%~1.new"
This program get all variables from the Batch file given in the parameter and renames they as "v#" with a sequential number. To use this program you must remove any setlocal command from the Batch file before pass it to this program. Of course, this detail may cause several pitfalls: although the called program have Delayed Expansion already Enabled, there is no way to activate any Disable/Enable delayed expansion that the Batch file may require in order to correctly run. Besides, the program changes all strings in the Batch file that are equal to any variable name, but even if this point could be fixed (and this is a big IF, because it requires to emulate the cmd.exe Batch file parser!) this program would still be useless: as I said in my other answer, renaming the variables in a Batch file have a very little effect on its performance. I just wrote it for fun!

Display parameters in reverse order

I need to create a batch file which will display on the screen up to 9 parameters, but displays them in reverse order. Name of the batch file is reverse11.bat
eg: C:\>REVERSE11.bat a b c d e <enter>
e d c b a REVERSE
I tried to do so like this, it kinda mess and didnt work. :(
SORT/R < %O > ANSWER
ECHO ANSWER
Whats wrong with it?
SORT sorts lines, not words, so you'll need to put each parameter on its own line.
setlocal enabledelayedexpansion
echo %1> unsorted.txt
echo %2>> unsorted.txt
echo %3>> unsorted.txt
:: etc...
sort /r unsorted.txt > sorted.txt
At this point you could display sorted.txt if you're okay with them all being on separate lines:
type sorted.txt
But if you want to get them all back onto a single line you'll have to process the file like this:
for /f %%a in (sorted.txt) do (
set out=!out! %%a
)
echo %out%
You must note that sort command Works on LINES, not WORDS! The Batch file below first divide the parameters in separate lines and store they in a temp file; the second part invoke sort /R on the file and collect its output lines in just one string:
#echo off
setlocal EnableDelayedExpansion
(for %%a in (%*) do echo %%a) > temp.txt
set output=
for /F "delims=" %%a in ('sort /R ^< temp.txt') do set output=!output! %%a
echo %output:~1%
del temp.txt
#ECHO OFF
SETLOCAL
SET reversed=%0
:loop
SET newparam=%1
IF NOT defined newparam ECHO %reversed%&GOTO :eof
SET reversed=%1 %reversed%
shift
GOTO loop
We start by setting the variable reversed to the value of the program name.
Set newparam to the value of the FIRST parameter (%1)
If that parameter exists,add it to the FRONT of the accumulated string, then SHIFT to move the parameters to one position lower (%2 becomes %1, %3 becomes %2, etc.) and loop back until...
the %1 parameter does NOT exist (because they've all been SHIFTed out) so echo the accumulated string in reversed and finish the routine.

Batch : Checking the number of parameters

I'd like to make sure that when calling my batch, no more than 2 parameters are passed.
Is there an easy way to check that, or do I have to call SHIFT as many times as needed until the parameter value is empty ?
You can simply test for existence of a third parameter and cancel if present:
if not "%~3"=="" (
echo No more than two arguments, please
goto :eof
)
But more specifically, there is no direct way of getting the number of arguments passed to a batch, short of shifting and counting them. So if you want to make sure that no more than 19 arguments are passed, then you need to do exactly that. But if the number of expected arguments is below 9 above method works well.
IF NOT "%3"=="" GOTO Too_Many_Args
Here is my small example for gathering and parsing a list of arguments and passing to external command:
#echo off
setlocal enabledelayedexpansion
if %1. EQU . (
echo %~0 [-t NUM] FILE [FILE...]
goto end
)
:args_loop
if "%~1" EQU "-t" (
set arg_t=%1
set arg_t_val=%2
shift
) else (
set files=!files! %1
)
shift
if %1. NEQ . goto args_loop
:args_loop_end
x:\path\to\external.exe %arg_t% %arg_t_val% %files%
:end

Resources