How to set BATCH if statement to select random predefined variables? - windows

How do I set my if statement to call on different predefined variables based on user input.
Example 1 is red 2 is orange 3 is blue 4 is random.
If user puts 1 they get red. If they put 2 they get orange. If they put 3 they get blue. If they put 4 they get either red, orange, or blue.

The following script shows one way of doing it, using arrays of colors and a method for doing double expansion on a variable (allowing for array access):
#setlocal enableextensions enabledelayedexpansion
#echo off
set color[1]=red
set color[2]=orange
set color[3]=blue
:loop
set /p inp="Enter a number [1=red, 2=orange, 3=blue, 4=random]: "
if "%inp%"=="4" set /a "inp = %RANDOM% %% 3 + 1"
call set color=%%color[%inp%]%%
if "%color%"=="" goto loop
endlocal && set color=%color%
For a more generalised solution, you can look at the following script, which better handles the prompting for arbitrary colors:
#setlocal enableextensions enabledelayedexpansion
#echo off
rem Clear out all color variables then create array.
set color=junk
for /f "delims==" %%a in ('set color') do set %%a=
set /a "count = 0"
set /a "count = count + 1" && set color[!count!]=red
set /a "count = count + 1" && set color[!count!]=orange
set /a "count = count + 1" && set color[!count!]=blue
set /a "count = count + 1" && set color[!count!]=green
set /a "next = count + 1"
rem Loop until color is valid.
:loop
echo.Choices:
for /l %%a in (1,1,%count%) do (
set value=!color[%%a]!
echo. %%a. !value!
)
echo. %next%. Random choice from above
set /p inp="Enter a number: "
rem set inp=1
rem Special handling, choose random value
if "%inp%"=="%next%" set /a "inp = %RANDOM% %% count + 1"
call set color=%%color[%inp%]%%
if "%color%"=="" goto loop
rem Exit local scope, "leaking" color value.
endlocal && set color=%color%

#ECHO Off
SETLOCAL
SET "choices=1=red 2=blue 3=green 4=random"
SET /p inp="[%choices%]: "
FOR /f %%a IN ('echo %choices: =^&echo %') DO SET /a maxchoice=%%a
IF "%inp%"=="%maxchoice%" SET /a inp=%RANDOM% %% (maxchoice - 1) +1
FOR /f "tokens=1,2" %%a IN ('echo %choices: =^&echo %') DO IF "%%a"=="%inp%" SET "hue=%%b"
ECHO %hue%
GOTO :EOF
Here's my version. All you need to do is follow the bouncing ball setting up choices and ensure that random is the last selection.

I started to write a comment as reply to new paxdiablo's solution, but it becomes too large, so I prefer to write my own solution here with all those points included:
#echo off
setlocal EnableDelayedExpansion
rem Create the color array
set n=0
for %%a in (red orange blue green) do (
set /A n+=1
set color[!n!]=%%a
)
set /A next=n+1
rem Show the available colors menu
echo Choices:
echo/
for /L %%i in (1,1,%n%) do echo %%i. !color[%%i]!
echo %next%. Random choice from above
echo/
rem Loop until color is valid
:loop
set /P "inp=Enter a number: "
if "%inp%" equ "%next%" set /A inp=%random% %% n + 1
set color=!color[%inp%]!
if "%color%" equ "" goto loop
echo/
echo Color: %color%

Related

Compare array of numbers in a batch script

I'm trying to compare a sequence of 9 numbers (separated by ,) using a batch file.
The comparison is always made by the corresponding sequence like:
mPrevious[0] <-> mCurrent[0]
mPrevious[1] <-> mCurrent[1]
I need to know if at least one sequece have changed. In the example bellow, 234 changed to 230 and 146 to 149.
The sketch I have so far is:
setlocal ENABLEDELAYEDEXPANSION
#echo off
set mPrevious=229,234,235,127,58,0,131,133,146
set mCurrent=229,230,235,127,58,0,131,133,149
for /f "tokens=1,2,3,4,5,6,7,8,9 delims=," %%a IN ('echo !mPrevious!') do (
)
The number of entries (currently 9) might change in the future. But for now they are just 9.
I'm not sure what is the proper way to do it inside a batch script.
#echo off
title <nul && title ...\%~nx0
setlocal enabledelayedexpansion
set "_mPrevious=229,234,235,127,58,0,131,133,146"
set "_mCurrents=229,230,235,127,58,0,131,133,149"
echo/!_mPrevious!|find "!_mCurrents!" >nul && (
endlocal & echo\Nothing changed^!! & goto :EOF )
for %%i in (!_mPrevious!)do set /a "_i+=1+0" && call set "_mPrev_!_i!=%%~i"
for %%j in (!_mCurrents!)do set /a "_j+=1+0" && call set "_mCurr_!_j!=%%~j"
if !_i! neq !_j! endlocal & echo\Varyables have different lengths^!! & goto :EOF
for /L %%L in (1 1 !_j!)do if !_mPrev_%%~L! neq !_mCurr_%%~L! echo\!_mPrev_%%~L! updated to: !_mCurr_%%~L!
endlocal && goto :EOF
Outputs:
234 updated to: 230
146 updated to: 149
One simple way to do this only if necessary and only if both variable has same length:
Make a first comparison if the variables are the same, there was a change in the values:
echo/!_mPrevious!|find "!_mCurrents!" >nul && (
endlocal & echo\Nothing changed^!! & goto :EOF )
And a second if they continue with the same length:
if !_i! neq !_j! endlocal & echo\Variables have different lengths^!! & goto :EOF
Obs.: 1. I prefer replace [ ] to one simple _
Obs.: 2. Also, change i+= to _i+=1+0, where no need predefined set command: set i=0
The FOR token delimiters are: <SPACE> <TAB> <NBSP> , ; =
Therefore, you can put it into a FOR loop, but it would fail if the content contained * or ?.
#echo off
====SETLOCAL EnableDelayedExpansion EnableExtensions
set/a"#=cnt=0"
::Define lists
set "mPrevious=229,234,235,127,58,0,131,133,146"
set "mCurrent=229,230,235,127,58,0,131,133,149"
FOR %%P in (!mPrevious!) do (
FOR %%C in (!mCurrent!) do (
if !cnt! equ !#! echo(%%P %%C
set/a"cnt+=1"
)
set/a"cnt=0,#+=1"
)
This is an approach using some self-expanding code:
#echo off
setlocal EnableDelayedExpansion
rem // Define constants here:
set "mPrevious=229,234,235,127,58,0,131,133,146"
set "mCurrents=229,230,235,127,58,0,131,133,149"
rem // Initialise auxiliary variables and indexes:
set "nPrevious=,%mPrevious%" & set /A "i=0"
set "nCurrents=,%mCurrents%" & set /A "j=0"
rem // Convert lists to arrays using self-expanding code:
set "_=%nPrevious:,=" & set /A "i+=1" & set "nPrevious[!i!]=%"
set "_=%nCurrents:,=" & set /A "j+=1" & set "nCurrents[!j!]=%"
rem // Verify availability of arrays:
> nul 2>&1 set nPrevious[ || set /A "i=0"
> nul 2>&1 set nCurrents[ || set /A "j=0"
rem // Determine minimal and maximal count:
if %j% gtr %i% (set /A "k=i, l=j" & set "_=#") else (set /A "k=j, l=i" & set "_=")
rem // Compare corresponding elements:
for /L %%K in (1,1,%k%) do if !nPrevious[%%K]! neq !nCurrents[%%K]! (
echo [%%K]: !nPrevious[%%K]! -^> !nCurrents[%%K]!
)
rem // Return removed or added elements:
set /A "k+=1" & for /L %%K in (!k!,1,%l%) do if defined _ (
echo [%%K]: --- -^> !nCurrents[%%K]!
) else (
echo [%%K]: !nPrevious[%%K]! -^> ---
)
endlocal
Sample output, relying on the data of the question:
[2]: 234 -> 230
[9]: 146 -> 149

Aligning output from batch file For loop

REM ************************ HIGH SCORES TABLE
**********************************************
:highscorestable
set /a count = 0
for /f "tokens=1,2,3 delims=-" %%i in (highscores.txt) do (
set hs=%%i
set hsn=%%j
set hsv=%%k
set hst=%%jscored %%iusing%%k
set hsn1=!hsn!
set hsv1=!hsv!
set hs1=!hs!
set hsn1= %hsn1%
set hsv1= %hsv1%
set hs1= %hs1%
echo %hsn1:~-15% %hsv1:~-15% %hs1:~-15%
set /a count+=1
if "!count!"=="5" goto :end
)
:end
echo.
pause
I'm pulling the first 5 lines from a text file using a For loop. My variables populate fine, however I'm struggling with the required alignment.
My ultimate end result should be:
James Commitment 300
Markos Excellence 290
Jeremy Si Party 50
What obvious thing am I missing here?
You could try this:
SetLocal EnableDelayedExpansion
REM **************************** HIGH SCORES TABLE ****************************
:highscorestable
Set "count=0"
For /F "UseBackQTokens=1-3Delims=-" %%i In ("highscores.txt") Do (
Set "hs=%%i"
Set "hsn=%%j"
Set "hsv=%%k"
Set "hst=%%jscored %%iusing%%k"
Set "hs= %%i "
Set "hsn1=%%j "
Set "hsv1=%%k "
Echo !hsn1:~,15!!hsv1:~,15!!hs:~-15!
Set/A count+=1
If "!count!"=="5" GoTo :end
)
:end
Echo(
Pause
Or without the possibly unnecessary variables:
SetLocal EnableDelayedExpansion
REM **************************** HIGH SCORES TABLE ****************************
:highscorestable
Set "count=0"
For /F "UseBackQTokens=1-3Delims=-" %%i In ("highscores.txt") Do (
Set "hs= %%i "
Set "hsn=%%j "
Set "hsv=%%k "
Set "hst=%%jscored %%iusing%%k"
Echo !hsn:~,15!!hsv:~,15!!hs:~-15!
Set/A count+=1
If "!count!"=="5" GoTo :end
)
:end
Echo(
Pause
In both cases, I've added the necessary SetLocal EnableDelayedExpansion line just in case it isn't in your script prior to your provided code.
Edit
You can also alter the code a little forego delayed expansion: (my preferred option)
REM **************************** HIGH SCORES TABLE ****************************
:highscorestable
For /F "Tokens=1-4Delims=:-" %%A In ('FindStr/N $ "highscores.txt"'
) Do If %%A LEq 5 (Set "hst=%%Cscored %%Busing%%D"
Set "hss= %%B"
Set "hsn=%%C "
Set "hsv=%%D "
Call Echo %%hsn:~,15%%%%hsv:~,15%%%%hss:~-10%%)
Echo(
Pause
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir"
SET "filename1=%sourcedir%\q46747991.txt"
REM ************************ HIGH SCORES TABLE
REM **********************************************
:highscorestable
set /a count = 0
SET "manyspaces= "
for /f "tokens=1,2,3 delims=-" %%i in (%filename1%) do (
set hs=%%k&CALL :align hs -8
set hsn=%%i&CALL :align hsn 15
set hsv=%%j&CALL :align hsv 10
ECHO !hsn!!hsv!!hs!
set /a count+=1
if "!count!"=="5" goto end
)
:end
echo.
GOTO :EOF
:align
IF %2 gtr 0 (
CALL SET "%1=%%%1%%%manyspaces%"
CALL SET "%1=%%%1:~0,%2%%"
) ELSE (
CALL SET "%1=%manyspaces%%%%1%%"
CALL SET "%1=%%%1:~%2%%"
)
GOTO :eof
I edited your results for a source file which I named to suit my system, hence the sequence of coulmns is different from your unpublished source. I changed the metavariable-assignment to suit.
The :align routine peels potatoes by recognising the second argument as the required column-width, positive for left-align and negative for right-align.
The variable manyspaces is set to an obvious value, of sufficient length to cope with the widest column required. Obviously, since it won't change once established, it's best set in the very beginning of the batch.
The routine uses the call set %%var%% method so that it will work regardless of whether delayedexpansion is invoked or not.
The mechanics are, for instance
CALL SET "%1=%%%1%%%manyspaces%"
with %1=fred
First, parse the command. %1 is replaced by fred and %% by %, yielding
set
"fred=
%fred%[spaces]"
So appends the space-string to the current value of the environment variable specified as %1
The second set - analyse similarly; result is assigned to the environment variable specified as %1
So the routine can be used to generate a fixed-width string, appropriately aligned using any ordinary variable, even if the variable has a value of nothing (ie. is undefined)

Batch: Replacement for goto loop

When I'm writing batch programs I tend to create a goto loop to set and display things.
Example:
#echo off & setlocal enabledelayedexpansion
:loop
if !num! GTR !max!
set /a "num=num+1"
echo display!num! = !display%num%!
goto :loop
I had a feeling that I can replace this with a for loop, but I had no success creating one that can replace the loop above. Does anyone know?
You can use a for /L loop.
for /L %%A in (!num!,1,!max!) do echo display%%A = !display%%A!
Where !num! is your starting number, !max! is your ending number, and 1 means count up by ones.
#echo off
setlocal enableextensions enabledelayedexpansion
rem Set limits
set /a "num=1", "max=10"
rem Prepare a set of variables to test
for /l %%a in (%num% 1 %max%) do set "display%%a=!random!"
rem Show the variables contents
for /l %%a in (%num% 1 %max%) do echo display%%a=!display%%a!

Sorting input numbers in batch file

Im trying to create a program in which you will enter 5 numbers randomly and will automatically shows the sorted entered numbers from lowest to highest. Here is my code.
#echo off
:main
set /p num1="Enter 1st :"
set /p num2="Enter 2nd :"
set /p num3="Enter 3rd :"
set /p num4="Enter 4th :"
set /p num5="Enter 5th :"
set /a high=0
set /a low=0
set /a ave=(num1+num2+num3+num4+num5)/5
if %num1% GTR %num2% (set /a high=%num1%) ELSE set /a high =%num2%
if %num3% GTR %high% (set /a high=%num3%)
if %num4% GTR %high% (set /a high=%num4%)
if %num5% GTR %high% (set /a high=%num5%)
if %num1% LSS %num2% (set /a low=%num1%) ELSE set /a low =%num2%
if %num3% LSS %low% (set /a low=%num3%)
if %num4% LSS %low% (set /a low=%num4%)
if %num5% LSS %low% (set /a low=%num5%)
ECHO.
ECHO Average: %ave%
ECHO Highest: %high%
ECHO Lowest: %low%
ECHO Input Numbers: %num1% %num2% %num3% %num4% %num5%
:end
set /p return="Continue?"
if "%return%"=="yes" GOTO main
GOTO exit
:exit
There are several ways to solve this problem. In the "one by one" method each variable must be compared vs. another one; this method is too much complex and is the one you used to read the values. You may also use sort command to sort the numbers (as Rafael tried in his answer); however, the numbers must be padded at left side with zeros in order to be correctly sorted.
When a certain process is repeated over several elements, the usual way to solve such a problem is via an array. You may find descriptions about array concept in several sites, like in Wikipedia. You may read how to use arrays in a Batch file at this post.
The Batch file below make good use of the fact that environment variables are kept automatically sorted; this way, the program just insert the numbers in an array and then take out its elements, so the sort process itself is not required:
#echo off
setlocal EnableDelayedExpansion
set N=5
:main
rem Delete array elements from previous cycle, if any:
for /F "delims==" %%a in ('set array[ 2^>NUL') do set "%%a="
rem Read the numbers; at same time, accumulate they in "ave" variable
rem and create the array with the proper index (with left zeros)
set ave=0
for /L %%i in (1,1,%N%) do (
set /P num="Enter number %%i :"
set /A ave+=num
set index=000000000!num!
set array[!index:~-10!]=!num!
)
set /A ave=ave/N
rem Assemble the output line and get low and high values
set "output="
set "low="
for /F "tokens=2 delims==" %%a in ('set array[') do (
if not defined low set low=%%a
set high=%%a
set output=!output! %%a
)
ECHO/
ECHO Average: %ave%
ECHO Highest: %high%
ECHO Lowest: %low%
ECHO Input Numbers: %output%
set /p return="Continue?"
if "%return%"=="yes" GOTO main
I was looking for an answer to a similar "sort numbers" question, and want to add example of using SORT command as an alternative for completeness. Note that using SORT requires writing a file to disk and seems less efficient:
#echo off
setlocal EnableDelayedExpansion
set "num_1=2" & set "num_2=5" & set "num_3=1" & set "num_4=18" & set "num_5=10"
for /f "tokens=2 delims==" %%i in ('set num_') do (set /a "var=1000000+%%i"
echo !var! >> nums.txt)
for /f %%j in ('sort nums.txt') do (set /a "var=%%j-1000000"
set "nums=!nums! !var!")
del /q nums.txt & echo !nums!
:eol

How to get the value from a dynamic variable in windows batch file

I am using Microsoft Windows XP [Version 5.1.2600]
Trying to do this.
Trying to create the variable dynamically and then read the value out of that variable in a loop.
#ECHO off
SET SQL1=TEST
SET SQL2=TEST1
SET SQL3=TEST2
SET SQL=SQL
SETLOCAL ENABLEDELAYEDEXPANSION
SET /A number=0
FOR /l %%A IN (1,1,3) DO (
SET /A number = number + 1
echo !number!
echo %SQL%!number!
)
endlocal
Output should be
1
test
2
test2
3
test3
I am getting
C:\temp>c.bat
1
SQL1
2
SQL2
3
SQL3
Please help!
Ugh.
First way (note that instead of your number variable, it uses the loop counter variable %%A):
#ECHO off
SET SQL1=TEST
SET SQL2=TEST1
SET SQL3=TEST2
SETLOCAL ENABLEDELAYEDEXPANSION
SET /A number=0
FOR /l %%A IN (1,1,3) DO (
SET /A number = number + 1
echo !number!
echo !SQL%%A!
)
endlocal
Second way (ugly but it does what you want):
#ECHO off
SET SQL1=TEST
SET SQL2=TEST1
SET SQL3=TEST2
SETLOCAL ENABLEDELAYEDEXPANSION
SET /A number=0
FOR /l %%A IN (1,1,3) DO (
SET /A number = number + 1
echo !number!
for %%i in (!number!) do (echo !SQL%%i!)
)
endlocal

Resources