I need to create an integer array that stops at a limit number and then goes on after another fixed value. Something like [1 2 3 4 5 10 11 12 13 14 15 16], where 5 is the limit number and 10 where it restarts.
Can you do something like set arr = [1:%lim%, 10:%max%] ?
#ECHO OFF
SETLOCAL
CALL :buildlist mylist 1 5 10 16
ECHO list is %mylist%
PAUSE
GOTO :eof
:: Produce a list of integers
:: %1 is listname
:: %2 is start value
:: %3 is end-value for range
:: %4 is restart-value
:: %5 is end-value
:buildlist
SET "%1="
FOR /L %%v IN (%2,1,%5) DO (
IF %%v leq %3 CALL SET "%1=%%%1%% %%v"
IF %%v geq %4 CALL SET "%1=%%%1%% %%v"
)
CALL SET "%1=%%%1:~1%%"
GOTO :eof
Batch really doesn't have arrays, but can simulate one with a little imagination.
The above routine produces a list of values to the specification, ready for processing by a for statement.
Although it's possible to use delayedexpansion, it's possible to avoid that facility. By CALL ing a set command, the command is parsed before execution so to decode the hieroglyphics, where %1 is the variable name "var"
SET "%1=%%%1%% %%v"
is processed as:
SET "var=%var% %v"
as %% is an escaped-%, and %v will be replaced by the value of the metavariable (loop-control variable) %%v
Similarly,
SET "%1=%%%1:~1%%"
is executed as
SET "var=%var:~1%"
which deletes the first character, which will be a space.
This problem is about several for /L commands really, although it could be described in terms of nested if commands or in other different ways...
There are several different methods to give the values of the for commands, and also to implement they. I think this is the simplest one:
#echo off
setlocal EnableDelayedExpansion
set ranges="1:5" "10:16"
set "list="
for %%a in (%ranges::= 1 %) do for /L %%i in (%%~a) do set "list=!list! %%i"
echo list=%list%
Note that this method also works with several (more than two) ranges of values.
PS - This result is a list, not an array
Batch doesn't have OR operators, so you'll have to make do with two separate if statements - one for the first set of numbers and one for the second set of numbers. You can, however, put both of those in the same for loop:
#echo off
setlocal enabledelayedexpansion
set "lim=5"
set "max=10"
REM Setting counter to -1 because we're going to increment the counter and then
REM use it, and arrays famously start at zero.
set "counter=-1"
for /L %%A in (1,1,16) do (
if %%A LEQ %lim% (
set /a counter+=1
set "array[!counter!]=%%A"
)
if %%A GEQ %max% (
set /a counter+=1
set "array[!counter!]=%%A"
)
)
REM Display the contents of the array just to prove it worked.
REM Alphabetic order is used, so array[10] and array[11] are going to print before array[1]
set array[
Related
I am writing a batch script and I am having trouble echoing a variable, here is the script,
#echo off
set num1=1
set num2=10
set /a "out=%num1%*%num2%"
echo %out%
pause`
The output I receive is 10 which makes sense but I want it to echo 'num1' ten times instead of multiplying 'num1' by 'num2'. So I want the output to be 1111111111.
Also I don't want to loop the command 10 times as I am putting the output into a text file with 'output>> file.txt' otherwise I will end up with this in the text file,
1
1
1
1
1
1
1
1
1
1
I want to end up with 1111111111, thank you.
#ECHO OFF
SETLOCAL
set num1=1
set num2=10
SET "out="&FOR /L %%a IN (1,1,%num2%) DO CALL SET "out=%%out%%%%num1%%"
echo %out%
GOTO :EOF
The SET "out=" and FOR /L %%a IN (1,1,%num2%) DO CALL SET "out=%%out%%%%num1%%" may be on separate lines if desired. Setting out to nothing is simply a safety measure to ensure that if it contains a value, it's cleared first.
The for /L performs the call command num2 times.
The call command executes SET "out=%out%%num1%" in a subprocess because each %% is interpreted as an escaped-% (% is the escape character for %) - "escaping" a character means turning off its special meaning.
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
Just to show a different method with set /A and a loop:
#echo off
set /A "num1=1,num2=10,out=0"
:loop
set /a "out*=10,out+=num1,num2-=1"
If %num2% gtr 0 goto :loop
echo %out%
pause
This is the simplest way to solve this problem, using Delayed Expansion.
#echo off
setlocal EnableDelayedExpansion
set num1=1
set num2=10
set "out="
for /L %%i in (1,1,%num2%) do set "out=!out!%num1%"
echo %out%
pause
PS - The multiply term is not exact in this case; perhaps "echo a variable the times indicated by another variable" be more clear...
If what you want is to print num1 num2 times in the same line you can do something like:
#echo off
set "num1=1"
set "num2=10"
(for /L %%i in (1,1,%num2%) do set /p "=%num1%" <nul
echo()>file.txt
The command set /p "=%num1%" <nul prints the text %num1% in the current line without the LF character. So num1 gets printed num2 times in the same line.
I have a text file with one string per line (only a couple lines total). This file needs to be searched and each string stored. The script will ultimately prompt the user to choose one of the stored strings (if more than one string/line is present in the file), but the for loop is iterating an extra time when I don't want it to.
I'm using a counter to check the number of iterations, and I also read that it's probably a NL CR regex issue, but the finstr /v /r /c:"^$" in the for file-set like in this post Batch file for loop appears to be running one extra time/iteration doesn't work for me (but I probably don't understand it correctly).
The "pref" term is because the strings are to be eventually used as a prefix of files in the same folder...
#echo off
setlocal enabledelayedexpansion
set /a x=1
for /f %%a in (sys.txt) do (
set pref!x!=%%a && echo %^%pref!X!%^% && set /a x+=1
)
echo last value of x = !x!
for /L %%a in (1,1,!x!) do (
echo !pref%%a!
)
REM The rest would be to prompt user to choose one (if multiple) and
REM then use choice as a prefix with a ren %%a %prefX%%%a
If the "sys.txt" contains three lines with strings A, B, C respectively, then the output I currently get is:
pref1
pref2
pref3
last value of x = 4
A
B
C
ECHO is off.
ECHO is off. is not desired, clearly.
You just need to change your increment structure like this. (set it before each line starting from a base of 0)
#Echo Off
Setlocal EnableDelayedExpansion
Set "i=0"
For /F "UseBackQ Delims=" %%A In ("sys.txt") Do (
Set/A "i+=1"
Set "pref!i!=%%A"
Echo(pref!i!)
Echo(last value of i = %i%
For /L %%A in (1,1,%i%) Do Echo(!pref%%A!
I am trying to create some sort of handler for arguments passed into my script. The idea is to handle "pairs" of arguments together. So, that is to say 1 and 2, 3 and 4, 5 and 6, and so on... (NOT 2 and 3, 4 and 5, 6 and 7, and so on...)
I think batch is very cool, but I am very new to batch. I am lost as how to do it. So far I have managed to successfully put the arguments into an array with the following script:
SETLOCAL EnableDelayedExpansion
set /a count+=1
set "params[%count%]=%~1"
shift
if defined params[%count%] (
goto :repeat
) else (
set /a count-=1
)
Now I just need to handle each of the elements in pairs. I don't know how to do this at all. I know how to handle the elements individually with for /F "tokens=2 delims==" %%s in ('set params[') do, but I don't know how to do pairs
on the side can someone explain what the delayed expansion thing is supposed to do?
just for clarity, by "handle" I mean to manipulate it and pass it on as an argument onto another script. this must be done in pairs.
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
:: remove variables starting $ or #
For %%b IN ($ #) DO FOR /F "delims==" %%a In ('set %%b 2^>Nul') DO SET "%%a="
SET /a count=0
SET "select=this is select's original value"
ECHO parameters received = %*
:loop
REM SET $... and #... to the parameter-pair.
SET $%count%=%1
SET #%count%=%2
IF NOT DEFINED $%count% GOTO process
IF NOT DEFINED #%count% ECHO "Incomplete pair"&GOTO :EOF
SET /a count +=1
:: shift twice to move 2 parameters
shift&shift&GOTO loop
:process
ECHO %count% pairs found
SET $
SET #
FOR /L %%a IN (1,1,%count%) DO (
REM note that this changes "select"
SET /a select=%%a - 1
CALL SET /a item1=%%$!select!%%
CALL SET /a item2=%%#!select!%%
ECHO pair %%a is !item1! and !item2! from $!select! and #!select! NOT $#%select%
)
ECHO ----------------- select = %select% ----------
SET /a count-=1
FOR /L %%a IN (0,1,%count%) DO (
REM note that this changes "select"
SET /a select=%%a + 1
ECHO pair !select! is !$%%a! and !#%%a! from $%%a and #%%a NOT $#%select%
GOTO :EOF
First, set up some initial values. The first for ensures that any variables starting $ or # are cleared.
Set $0 and #0 to the first and second parameters. If $0 is not set, then we have no further data, so process what we have. If $0 is set but #0 is not, we don't have a pair so complain and terminate.
If both #0 ans $0 are set, increment count, shift the parameters twice and run around the loop. The next pair will be placed in $/#1 and so on until the parameters are exhausted.
Now show the count of parameters and the $ and # values that have been set.
Next loop show unnecessary manipulation of the variables.
First set select to 1 less than the loop-counter.
then use a parsing trick to get the value of $[current value of select]. The line translates to "SET /a item1=%$n%" where n is the run-time value of select - ie. the value as it changes through the loop.
then show the results. item1 is changed within the block (parenthesised series of statements) so we need !item1 to access that changed value - %item1% would give us the value as it was when the loop was parsed. You can see that from the report for select on the same line.
Last loop is the same idea - but varying %%a from 0 to (count-1). Observe that select in the dashed echo line has the last value it was assigned in the previous loop.
So, once again we change select within the block, so we see a difference between !select! and %select%. We can also access $0 directly using !$%%a! - that is the current value of the variable [$ strung with the current value of the loop-control %%a]
#echo off
setlocal EnableDelayedExpansion
rem Store arguments one by one
set "count=0"
:repeat
set /A count+=1
set "param[%count%]=%~1"
shift
if "%~1" neq "" goto repeat
rem Show arguments in pairs
set /A countMIN1=count-1
for /L %%i in (1,2,%countMIN1%) do (
set /A j=%%i+1
for %%j in (!j!) do echo !param[%%i]! !param[%%j]!
)
For further details, see: Arrays, linked lists and other data structures in cmd.exe (batch) script
As part of a program I'm making, I want to be able to 'plot' or 'mark' random points on a grid. In doing so I need to generate a random number(x) to determine the number of plots- and then x amount of different random numbers representing coordinates. At the moment I have the following code:
#echo off
:obno
set /a r1=%random%
if %r1% gtr 10 goto obno else (
goto ob
)
:ob
for /L %%R in (1,1,%r1%) do set /a n%%R=%random%*240/32678+1
echo %r1%
echo %n1% %n2% %n3% %n4% %n5% %n6% %n7% %n8% %n9% %n10%
pause
What happens here is I end up with x amount of the same random number instead of different ones
i.e. ouput:
5
108 108 108 108 108
so any help would be greatly appreciated!
In a loop you should be using delayed expansion e.g.
SETLOCAL ENABLEDELAYEDEXPANSION and using !RANDOM! instead of %RANDOM%
And...
%RANDOM% gives you a random number between 0 and 32767.
Using an expression like below you can change the range to anything you like (here the range is [1…100] instead of [0…32767]).
SET /A test=%RANDOM% * 100 / 32768 + 1
And below will produce number between 1~100
set /a num=%random% %%100 +1
To get a random number between 0 and 9 on the command line, use
set /a "rand=%random% % 10"
If used in a batch file then double the modulo operator
set /a "rand=%random% %% 10"
Using your code, you can use a call and double the % to make %%random%%
#echo off
:obno
set /a r1=%random%
if %r1% gtr 10 goto obno else (
goto ob
)
:ob
for /L %%R in (1,1,%r1%) do call set /a n%%R=%%random%%*240/32678+1
echo %r1%
echo %n1% %n2% %n3% %n4% %n5% %n6% %n7% %n8% %n9% %n10%
pause
Here is a simliar post to generate random numbers in a batch file
You need to include the following at the start of your batch file
setlocal EnableDelayedExpansion
Random variable not changing in "for" loop in windows batch file
#ECHO OFF
SETLOCAL
:obno
set /a r1=%RANDOM% %% 10 + 1
:ob
for /L %%R in (1,1,%r1%) do CALL set /a n%%R=%%random%% %%%% 240 + 1
echo %r1%
echo %n1% %n2% %n3% %n4% %n5% %n6% %n7% %n8% %n9% %n10%
GOTO :EOF
Since %random% produces a random number 0..32767, your original code will waste a lot of time waiting for a number between 0 and 10. Also, it may generate 0.
The % operator is used for 'modulus'. Within a batch, you need to double it, so %random% %% 10 produces 0..9
By CALLing a command, you can avoid the need to setlocal enabledelayedexpansion BUT you also need to redouble any % you use, hence the doubling around random and quadrupling for the mod operator.
The setlocal isn't strictly necessary, BUT what it does is back out enviroment changes when the batch ends, so r1,n1..n10 simply no longer exist in the environment once the batch finishes. If you OMIT the setlocal then any variable set in one run will REMAIN set for a subsequent run, so if your first run sets n5 (and also therefore n1..n4) then if the second run sets r1=2, n3..n5 would be displayed with the stale data from the previous run.
Finally, if you allow r1=0 then since none of n1..n10 are set, the echo %n1%... command will be resolved to echo, will will show the echo status (Echo is on/off). This can be overcome by using the syntax echo(%n1%.... The character directly following the O has been found to do some strange things.
echoDot for instance is traditional for a blak newline. The ( may look odd and appear to 'unbalance' the nesting, but works quite happily to produce a newline with empty arguments.
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