I want to create a batch file on Windows that can let the user enter only a number between 1-31... I could use this number later in the batch file... It is possible ?
I tried this
set /P "month=Enter the month of the year : "
findstr /i %month% %file% | sort /+24
Thanks :)
#echo off
:try_again
set /P "month=Enter the month of the year : "
echo %month%|findstr /r "[^0-9]" && (
echo enter a number
goto :try_again
)
::clears the leading zeroes.
cmd /c exit /b %month%
set /a month=%errorlevel%
if %month% gtr 31 (
echo enter a number between 1 and 31
goto :try_again
)
if %month% lss 1 (
echo enter a number between 1 and 31
goto :try_again
)
?
Well, these two options are entirely different:
Let the user enter anything; then, check if the input is a number between 1 and 12 and retry the input if it is not.
Let the user just enter a number between 1 and 12.
The Batch file below implement the second method:
#echo off
setlocal EnableDelayedExpansion
echo Precede numbers 1 and 2 by a zero
set /P "=Enter a month: " < NUL
choice /C 0123456789 > NUL
set /A "number=%errorlevel%-1"
if %number% gtr 1 echo %number% & goto continue
set /P "=%number%" < NUL
if %number% equ 0 (
choice /C 12 > NUL
set "digit2=!errorlevel!"
) else (
choice /C 012 > NUL
set /A "digit2=!errorlevel!-1"
)
echo %digit2%
set /A "number=number*10+digit2"
:continue
echo/
echo Number read: %number%
A very simple but efficient method I use when I need a non-zero numeric input is the following code (note that this verifies the user entry afterwards):
:RETRY_RESET
rem /* Since zero is considered as invalid, preset variable to `0` to
rem not keep the former value in case the user just presses ENTER;
rem you could also define a non-zero default value here optionally: */
set /A NUMBER=0
:RETRY_REUSE
rem // Display prompt now:
set /P NUMBER="Please enter a positive number: "
rem /* Convert entry to a numeric value; everything up to the first
rem numeral is converted to a numeric value, except leading SPACEs
rem or TABs are ignored and signs `+` and `-` are recognised: */
set /A NUMBER+=0
rem /* Caution: numbers with leading `0` are converted to octal ones!
rem since `8` and `9` are not valid octal numerals, entries with
rem such figures and leading zeros are converted to `0`! */
rem // Verify entry:
if %NUMBER% EQU 0 goto :RETRY_RESET
rem // Do something with `%NUMBER%` at this point...
rem /* Afterwards you can jump to `:RETRY_RESET` to enter another number;
rem alternatively, jump to `:RETRY_REUSE` to maintain the former entry
rem in case the user just presses ENTER... */
This will not fail for any entry you can think of because the variable NUMBER holding the value is never expanded before it is converted to a true number by set /A NUMBER+=0.
The script recognises + and - signs correctly. Leading white-spaces are ignored. Besides all those, everything up to the first non-numeric figure is converted to a number; so for instance, an entry like SPACE+15.75k is converted to 15 as the . is not a numeral.
The disadvantage of this approach is that leading zeros may lead to unexpected results as set /A interpretes numbers with such as octal ones; so for instance, 012 is converted to (decimal) 10, and 08 and 09 are converted to 0 as 8 and 9 are not valid octal digits.
A good point though could be the fact that hexadecimal numbers are recognised correctly in case they are prefixed with 0x; for example, 0x18 is converted to 24; 0xAS becomes 10 (as S is not hex.).
A safe and simple manner for performing this task is the use of the Set /A command in conjunction with the || conditional operator and a :label to return to for when invalid input is entered.
A number of tests can be performed on the input value using set /a without expanding the variables content in a manner that leaves your code vulnerable to code injection.
An example:
#Echo off
Call:ValidNum $RV 31
Call:ValidNum "" "" 5
Set $
Goto:Eof
:ValidNum [returnvar] [max] [min]
SETLOCAL
Set "sign=-1"
:VNumIn
%= ensure nul value =%
Set "input="
%= define max value =%
2> nul Set /a "max=%~2" || Set "max=2147483647"
%= define min value =%
2> nul Set /a "min=%~3" || Set "min=1"
Set /p "input=Enter a number GEQ %min% LEQ %max%: "
%= Input Testing. input +/- , input > min , input < max , Hex/Octal for comparison =%
2>nul Set /a "1/(sign-(input>>31))","max/(input/min)","1/(max/input)","HexOct=input" || Goto:VNumIn
%= compare assignments to block hex, octal and leading 0's =%
If not "%Input%"=="%HexOct%" Goto:VNumIn
( %= return value in $Num if no arg 1, else return in Arg1 =%
ENDLOCAL & Set "%~1=%input%" 2> nul || Set "$Num=%input%"
Goto:Eof
)
The following script is sort of a mixture of both restricting characters/key during entry and verifying characters/value after entry. The code is quite complex but it is very flexible and also safe. Here is it:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here (`findstr` reg. expr.):
set "WHITE=[0-9]" & rem // (positive list, accepted characters)
set "BLACK=[^`^]" & rem // (negative list, rejected characters)
set "LENGTH=2" & rem // (optional limit for length of entry)
rem // resolve length limit:
set /A LENGTH-=1
if %LENGTH% LSS 0 set "LENGTH="
rem // Retrieve back-space character:
for /F %%C in ('echo prompt $H ^| cmd') do set "BS=%%C"
:HOOK
rem // Display prompt:
echo(Please enter something:
set "ENTRY="
:LOOP
rem // Use `xcopy /W` to capture a single key stroke:
set "KEY="
for /F "delims=" %%K in ('2^> nul xcopy /L /W "%~f0" "%~f0"') do (
if not defined KEY set "KEY=%%K"
)
set "KEY=%KEY:~-1%"
rem // Leave loop in case ENTER has been pressed:
if not defined KEY goto :NEXT
rem // Disallow `"` to avoid syntax errors (`if`, no del. exp.):
set "KEY=%KEY:"=%" & rem "
rem // Disallow `=` to avoid syntax errors (`set /P`):
if "%KEY%"=="=" set "KEY="
rem // Disallow ` ` (tabulator):
if "%KEY%"==" " set "KEY="
rem // Optional additional filter (include):
if defined WHITE (
(echo("%KEY%" | > nul findstr /R /C:"%BS%" /C:"%WHITE%") || (
set "KEY="
)
)
rem // Optional additional filter (exclude):
if defined BLACK (
(echo("%KEY%" | > nul findstr /R /C:^"\"%BLACK%\"^") || (
set "KEY="
)
)
rem // In general, display string equals pressed key:
set "DISPLAY=%KEY%"
rem // Avoid space in display text (ignored by `set /P`):
if "%KEY%"==" " set "DISPLAY=_%BS% "
rem // Force to clear preceding character upon back-space:
if "%KEY%"=="%BS%" (
set "DISPLAY=%BS% %BS%"
if defined ENTRY set "ENTRY=%ENTRY:~,-1%"
set "KEY="
)
rem // Ignore any more character if length limit is reached:
set "TEST=%ENTRY%"
if defined LENGTH if defined ENTRY (
call set "TEST=%%ENTRY:~,%LENGTH%%%"
)
if not "%TEST%"=="%ENTRY%" (
set "KEY=" & set "DISPLAY="
)
set "ENTRY=%ENTRY%%KEY%"
rem // Show display text:
< nul set /P ="%DISPLAY%"
goto :LOOP
:NEXT
echo(
rem /* Verify the entry; for instance,
rem check numeric value after removal of leading zeros: */
cmd /C exit %ENTRY%
set /A ENTRY=%ErrorLevel%
set /A ENTRY+=0 & rem // (conversion to true numeric value)
if %ENTRY% LEQ 0 goto :HOOK
if %ENTRY% GTR 12 goto :HOOK
rem // Do something with the entry (display):
echo(You entered this value:
< nul set /P ="%ENTRY%"
echo(
endlocal
exit /B
The core of the script is the xcopy /L /W command which takes a single key stroke (/W) and does no copying (/L). Its output is captured by a for /F loop to get the current key or character. For displaying < nul set /P is used with nothing sent into its prompt but the message text displayed, which is not terminated by a line-break unlike echo. Consult also the comments (rem) in the code.
The script can be configured in the Define constants here block at the top:
variable WHITE defines a positive character set for the findstr command, one of which a character/key must equal; set to an empty string to disable this set; for our situation, [0-9] is suitable as it defines to accept only numeric figures;
variable BLACK defines a negative character set for the findstr command, one of which a character/key must not equal; set to an empty string to disable this list; since there is already WHITE defined, BLACK is not needed; the first character within the brackets [ ] must be a caret ^ so that the given characters are truly rejected; if the sets in WHITE and BLACK overlap, the latter takes precedence;
variable LENGTH defines the greatest length of the entry, so if the given number of characters have been supplied, no more are accepted; you can delete the last character though by the ←— key; since we need a two-digit numeric value, 2 is the value of choice here;
Related
I make a game for fun with batch script but in this code i have an error message "/100 unexpected at this time" i really don't understand why? Please help me!!
#echo off
mode con cols=110 lines=32
setlocal enabledelayedexpansion
set npctier=0
goto randomnpc
:randomnpc
if %npctier% EQU 0 (
set npctype=Wooden Dummy
set /a npclvl=%random% %% 5+1
set /a npchp=%npclvl% * 100
set /a npcdmg=0
set /a npcdef=(%npchp%*5)/100
set /a npcxp=%npclvl%*100 )
:combatchoice
echo.
echo. You see %npctype% level %npclvl%.
echo.
echo. The %npctype%'s Health: %npchp% HP
echo.
goto main
I recommend to first open a command prompt, run set /? and read the output help carefully and completely from top of first to bottom of last page. There is explained:
Any non-numeric strings in the expression are treated as environment variable names whose values are converted to numbers before using them. If an environment variable name is specified but is not defined in the current environment, then a value of zero is used. This allows you to do arithmetic with environment variable values without having to type all those % signs to get their values.
So there can be written just set /A npchp=npclvl * 100 as npclvl inside the arithmetic expression is interpreted as name of an environment variable and the command line works even on being inside a command block starting with ( and ending with matching ) without usage of delayed environment variable expansion.
Then run cmd /? and read again the output help carefully and completely from top of first to bottom of last page. There is explained that a file name (or any other argument string) containing a space one of these characters &()[]{}^=;!'+,`~ must be enclosed in " to get those characters interpreted as literal characters of an argument string.
Please read also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
For that reason the command line set /a npcdef=(%npchp%*5)/100 should be written with one of the following notations:
set /A npcdef=npchp*5/100
set /A npcdef=npchp * 5 / 100
set /A "npcdef=(npchp*5)/100"
set /A npcdef=(npchp*5^)/100
The caret character ^ escapes the next character for being interpreted as literal character except the next character is % which must be escaped with %.
The recommendations posted on DosTips forum topic ECHO. FAILS to give text or blank line - Instead use ECHO/ should be also taken into account on writing batch files which output empty lines.
The batch file with the main improvement of changing the IF condition to avoid completely the usage of a command block.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
%SystemRoot%\System32\mode.com con cols=110 lines=32
set npctier=0
set "npctype=Wooden Dummy"
:randomnpc
if %npctier% NEQ 0 goto CombatChoice
set /A npclvl=%random% %% 5 + 1
set /A npchp=npclvl*100
set npcdmg=0
set /A npcdef=npchp*5/100
set /A npcxp=npclvl*100
:CombatChoice
echo/
echo You see %npctype% level %npclvl%.
echo/
echo The %npctype%'s health: %npchp% HP
echo/
endlocal
See also Why is no string output with 'echo %var%' after using 'set var = text' on command line? It contains several hints on how to use command SET not written in help/documentation of this command.
If you wish to maintain the same structure, then something lke this would be better:
#Echo Off
SetLocal EnableExtensions
%__APPDIR__%mode.com 110, 32
Set "npctier=0"
:randomnpc
If %npctier% Equ 0 (
Set "npctype=Wooden Dummy"
Set "npcdmg=0"
Set /A "npclvl=(%RANDOM% %% 5) + 1"
Set /A "npchp=npclvl * 100, npcdef=npclvl * 5, npcxp=npchp"
)
:combatchoice
Echo=
Echo You see %npctype% level %npclvl%.
Echo=
Echo The %npctype%'s Health: %npchp% HP
Echo=
GoTo main
You should note, that it is possible to define multiple values using Set /A in one arithmetic instruction
Before you get too carried away with creating a lengthy script which repeats code, I think it's worthwhile introducing you to batch macro's.
Macro's in Batch are commands or command blocks assigned to variables, and through the use of For loops combined with If / Else conditioning can be used to Capture arguments allowing variables to be used as Functions.
The below example contains two variations of a macro that can be used to easily generate different encounter types with minimal repetative scripting whilst also avoiding the use of inefficient calls to functions.
Macro's must be defined prior to delayed expansion being enabled as variables referenced during macro execution are defined using ! expansion so that the value the macro is parsed with at expansion is the value of the variable at the time of parsing, not it's value during definition.
#echo off
mode con cols=110 lines=32
(Set \n=^^^
%= macro newline DNR =%
)
(Set LF=^
%= Linefeed DNR =%)
rem Example 1: npc generation; Fixed Formula macro
rem USAGE: %npc{generic}%{Npc name / type}{npc max level}{npc multiplier}{damage value or formula}
Set npc{generic}=For %%n in (1 2)Do if %%n==2 (%\n%
For /F "Tokens=1,2,3,4 Delims={}" %%G in ("!Params!")Do (%\n%
Set "npctype=%%G" %\n%
Set /a "npclvl= !random! %% %%H + 1" %\n%
Set /a "npchp= !npclvl! * %%I" %\n%
Set /a "npcdmg= %%J" %\n%
Set /a "npcdef= ( !npchp! * %%H ) / %%I" %\n%
Set /a "npcxp=!npclvl! * %%I" %\n%
echo/Enemy: !npctype!!LF!level: !npclvl!!LF!HP: !npchp!!LF!Damage: !npcdmg!!LF!Defence: !npcdef!!LF!XP: !npcxp!!LF!%\n%
)%\n%
) Else Set Params=
rem Example 2: npc generation; Supplied Formula macro
rem USAGE: %npc{boss}%{Npc name / type}{level value or formula}{hp value or formula}{damage value or formula}{defense value or formula}{xp value or formula}
Set npc{boss}=For %%n in (1 2)Do if %%n==2 (%\n%
For /F "Tokens=1,2,3,4,5,6 Delims={}" %%G in ("!Params!")Do (%\n%
Set "npctype=%%G" %\n%
Set /a "npclvl= %%H " %\n%
Set /a "npchp= %%I " %\n%
Set /a "npcdmg= %%J " %\n%
Set /a "npcdef= %%K " %\n%
Set /a "npcxp= %%L " %\n%
echo/Enemy: !npctype!!LF!level: !npclvl!!LF!HP: !npchp!!LF!Damage: !npcdmg!!LF!Defence: !npcdef!!LF!XP: !npcxp!!LF!%\n%
)%\n%
) Else Set Params=
rem enable delayed expansion after macro definitions
setlocal enableextensions enabledelayedexpansion
:randomnpc
%npc{generic}%{Wooden Dummy}{5}{100}{0}
Pause
%npc{boss}%{Dragon}{!random! %% 10 + 10}{npclvl * 200}{npchp / 20}{npchp / (npclvl / 2)}{npclvl * 150}
Endlocal
Goto :Eof
So my code here:
#echo off
pause
setlocal ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
set alfanum=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
:a
timeout /t 1
set generator=
FOR /L %%b IN (0, 1, 5) DO (
SET /A rnd_num=!RANDOM! * 62 / 32768 + 1
for /F %%c in ('echo %%alfanum:~!rnd_num!^,1%%') do set generator=!generator!%%c
)
echo %generator%
goto generator
Is slightly broken because sometimes "ECHO" shows up in the middle of generating. for example:
vMECHO8ECHOE <<
DQTGv0
aECx5i
lLECHOO3H <<
cOd4ECHOg <<
6950pC
Help? running on Windows 10 CMD.
Substring modification is 0 indexed. ensure the index you use is EQU to the number of characters - 1
FOR /L %%n IN (0,1,5)Do For /F "delims=" %%c In ( 'SET /A "_r=!RANDOM! %% 62"' )Do (
Set "generator=!generator!!alfanum:~%%c,1!"
)
Remove the + 1 from your set /A command line and you will be fine:
set /A "rnd_num=!RANDOM!*62/32768"
Which can be simplified to:
set /A "rnd_num=!RANDOM!%%62"
You actually point past the string in !alfanum!, because the position index for sub-string expansion is zero-based, meaning 0 points to the first character, and 61 to the last one in your 62-character string. Your current code returns 62 as the greatest possible random number, so the sub-string expansion returns an empty string, which lets the echo command in your for /F loop output ECHO is {on|off}., resulting in the result ECHO due to the default token string tokens=1 of for /F.
Anyway, The for /F loop is not necessary, just replace it by the line:
rem /* The standard `for` loop ensures that the value of `!rnd_num!` is expanded
rem BEFORE delayed sub-string expansion of `!alfanum:~...!` occurs: */
for %%a in (!rnd_num!) do set "generator=!generator!!alfanum:~%%a,1!"
Or (slower):
rem /* The `call` command ensures that the sub-string of `%alfanum:~...%`
rem is expanded AFTER delayed expansion of `!rnd_num!` occurs: */
call set "generator=!generator!%%alfanum:~!rnd_num!,1%%"
in a Windows cmd batch file (.bat), how do i pad a numeric value, so that a given value in the range 0..99 gets transformed to a string in the range "00" to "99". I.e. I'd like to having leading zeros for values lower than 10.
There's a two-stage process you can use:
REM initial setup
SET X=5
REM pad with your desired width - 1 leading zeroes
SET PADDED=0%X%
REM slice off any zeroes you don't need -- BEWARE, this can truncate the value
REM the 2 at the end is the number of desired digits
SET PADDED=%PADDED:~-2%
Now PADDED holds the padded value. If there's any chance that the initial value of X might have more than 2 digits, you need to check that you didn't accidentally truncate it:
REM did we truncate the value by mistake? if so, undo the damage
SET /A VERIFY=1%X% - 1%PADDED%
IF NOT "%VERIFY%"=="0" SET PADDED=%X%
REM finally update the value of X
SET X=%PADDED%
Important note:
This solution creates or overwrites the variables PADDED and VERIFY. Any script that sets the values of variables which are not meant to be persisted after it terminates should be put inside SETLOCAL and ENDLOCAL statements to prevent these changes from being visible from the outside world.
If you are confident that the number of digits in your original number is always <= 2, then
set "x=0%x%"
set "x=%x:~-2%"
If the number may exceed 2 digits, and you want to pad to 2 digits, but not truncate values larger then 99, then
setlocal enableDelayedExpansion
if "%x%" equ "%x:~-2%" (
set "x=0%x%"
set "x=!x:~-2!"
)
Or without delayed expansion, using an intermediate variable
set paddedX=0%x%
if "%x%" equ "%x:~-2%" set "x=%paddedX:~-2%"
The nice thing about the above algorithms is it is trivial to extend the padding to any arbitrary width. For example, to pad to width 10, simply prepend with 9 zeros and preserve the last 10 characters
set "x=000000000%x%"
set "x=%x:~-10%"
TO prevent truncating
set paddedX=000000000%x%
if "%x%" equ "%x:~-10%" set "x=%paddedX:~-10%"
The single line
IF 1%Foo% LSS 100 SET Foo=0%Foo%
will get you what you want for numbers in the range that you specify. It does not change values in the subset 0-9 if they are already single-padded.
Previous answers had explained all the existent methods to pad a value with left zeros; I just want to add a small trick I used to do that in an easier way. What had not been enough mentioned in previous answers is that in most cases, the value that will be padded is incremented inside a loop and that the padded value is just used to display it (or similar tasks, like renames). For example, to show values from 00 to 99:
set x=0
:loop
rem Pad x value, store it in padded
set padded=0%x%
set padded=%padded:~-2%
rem Show padded value
echo %padded%
set /A x+=1
if %x% leq 99 goto loop
If this is the case, the value of the variable may be used for both control the loop and display its padded value with no modification if its limits are appropriately translated. For example, to show values from 00 to 99:
set x=100
:loop
rem Show padded value
echo %x:~-2%
set /A x+=1
if %x% leq 199 goto loop
This method works also with any number of left zeros to pad.
Antonio
OK GUYS i have found a solution, compressing it down as simple as possible.
#echo off
title pad numbers
set num=0
set zero= 000
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero= 00
if /i %num% GTR 99 set zero= 0
if /i %num% GTR 999 set zero=
echo %zero%%num%
goto loop
this will display your count up number using 4 digits. but the code can be altered to use 2 digits as shown below.
#echo off
title pad numbers
set num=0
set zero= 0
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero=
echo %zero%%num%
goto loop
if you want to set it as a displayable single variable...
#echo off
title pad numbers
set num=0
set zero= 0
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero=
set %zero%%num%=number
echo %number%
goto loop
if you want it to count up in seconds...
#echo off
title pad numbers
set num=0
set zero= 0
:loop
#set /a num=%num%+1
if /i %num% GTR 9 set zero=
set %zero%%num%=number
echo %number%
ping localhost -n 2 >nul
goto loop
i hope this was a great help ^^
This example uses a for loop to demonstrate, but the logic is the same even if you were to use it without the loop. Just echo a 0 in front if the number is less than 10.
setlocal enabledelayedexpansion
for /l %%a in (1,1,40) do (
set n=%%a
if !n! lss 10 (
echo 0!n!
) else (
echo !n!
)
)
pause >nul
#echo off
rem .
rem counter example - with and without padding (up to 260 leading 0s which should be enough for most filenames)
rem .
rem we assume values given are valid
rem additional error checking could be done to make sure they are numbers
rem and to ensure that starting is less than ending
rem and that the number of ending digits is not greater than the number of padding digits
rem .
if "%2"=="" (
echo.
echo usage: %~nx0 [starting number] [ending number] [pad]
echo example: %~nx0 0 19 will output numbers 0 to 19 each on a new line
echo example: %~nx0 3 12 8 will output numbers 3 to 12 each on a new line padded to 8 digits
echo.
goto end
)
rem .
setlocal enabledelayedexpansion
if "%3"=="" (
for /l %%x in (%1, 1, %2) do (
echo.%%x
)
) else (
set "mynum="
for /l %%x in (%1, 1, %2) do (
call set "mynum=00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%%x"
call set "mynum=%%mynum:~-%3%%"
call echo.%%mynum%%
)
)
:end
It is much easier:
set build_b=00%your_value%
set padded_value=%build_b:~-2%
I need to implement a function in a Windows batch script to get the LastIndexOf a character into a given string.
For example: Given the following string, I need to get the last index of character '/':
/name1/name2/name3
^
So I need to get the value:
12
Joey's solution works, but the character to find is hard coded, and it is relatively slow.
Here is a parametized function that is fast and can find any character (except nul) within the string. I pass the name of variables containing the string and the character instead of string literals so that the function easily supports all characters.
#echo off
setlocal
set "test=/name1/name2/name3"
set "char=/"
::1st test simply prints the result
call :lastIndexOf test char
::2nd test stores the result in a variable
call :lastIndexOf test char rtn
echo rtn=%rtn%
exit /b
:lastIndexOf strVar charVar [rtnVar]
setlocal enableDelayedExpansion
:: Get the string values
set "lastIndexOf.char=!%~2!"
set "str=!%~1!"
set "chr=!lastIndexOf.char:~0,1!"
:: Determine the length of str - adapted from function found at:
:: http://www.dostips.com/DtCodeCmdLib.php#Function.strLen
set "str2=.!str!"
set "len=0"
for /L %%A in (12,-1,0) do (
set /a "len|=1<<%%A"
for %%B in (!len!) do if "!str2:~%%B,1!"=="" set /a "len&=~1<<%%A"
)
:: Find the last occurrance of chr in str
for /l %%N in (%len% -1 0) do if "!str:~%%N,1!" equ "!chr!" (
set rtn=%%N
goto :break
)
set rtn=-1
:break - Return the result if 3rd arg specified, else print the result
( endlocal
if "%~3" neq "" (set %~3=%rtn%) else echo %rtn%
)
exit /b
It wouldn't take much modification to create a more generic :indexOf function that takes an additional argument specifying which occurance to find. A negative number could specify to search in reverse. So 1 could be the 1st, 2 the 2nd, -1 the last, -2 penultimate, etc.
(Note: I'm assuming Windows batch files because, frankly, I have only seen a single question asking for an actual DOS batch file here so far. Most people simply misattribute “DOS” to anything that has a window of gray-on-black monospaced text without knowing what they're actually talking of.)
Just loop through it, updating the index as you go:
#echo off
setlocal enabledelayedexpansion
set S=/name1/name2/name3
set I=0
set L=-1
:l
if "!S:~%I%,1!"=="" goto ld
if "!S:~%I%,1!"=="/" set L=%I%
set /a I+=1
goto l
:ld
echo %L%
I know this question is a bit old now, but I needed a function that could find the location of a substring (of any length) within a string, and adapted dbenham's solution for my purposes. This function also works with individual characters within a string, as asked for in the original question, and can search for specific instances (as suggested by dbenham).
To use this function, the actual strings must be passed. Dbenham does note that this supports fewer characters than passing the actual variables, but I find that this variant is more reuseable (especially with pipes).
The third argument takes the instance that should be found, with negative numbers specifying to search from the end. The index returned is the offset from the start of the string to the first character in the substring.
#ECHO off
SET search_string=sub
CALL :strIndex "The testing subjects subjects to testing." "%search_string%" -2
ECHO %ERRORLEVEL%
PAUSE
EXIT
:strIndex string substring [instance]
REM Using adaptation of strLen function found at http://www.dostips.com/DtCodeCmdLib.php#Function.strLen
SETLOCAL ENABLEDELAYEDEXPANSION
SETLOCAL ENABLEEXTENSIONS
IF "%~2" EQU "" SET Index=-1 & GOTO strIndex_end
IF "%~3" EQU "" (SET Instance=1) ELSE (SET Instance=%~3)
SET Index=-1
SET String=%~1
SET "str=A%~1"
SET "String_Length=0"
FOR /L %%A IN (12,-1,0) DO (
SET /a "String_Length|=1<<%%A"
FOR %%B IN (!String_Length!) DO IF "!str:~%%B,1!"=="" SET /a "String_Length&=~1<<%%A"
)
SET "sub=A%~2"
SET "Substring_Length=0"
FOR /L %%A IN (12,-1,0) DO (
SET /a "Substring_Length|=1<<%%A"
FOR %%B IN (!Substring_Length!) DO IF "!sub:~%%B,1!"=="" SET /a "Substring_Length&=~1<<%%A"
)
IF %Substring_Length% GTR %String_Length% GOTO strIndex_end
SET /A Searches=%String_Length%-%Substring_Length%
IF %Instance% GTR 0 (
FOR /L %%n IN (0,1,%Searches%) DO (
CALL SET StringSegment=%%String:~%%n,!Substring_Length!%%
IF "%~2" EQU "!StringSegment!" SET /A Instance-=1
IF !Instance! EQU 0 SET Index=%%n & GOTO strIndex_end
)) ELSE (
FOR /L %%n IN (%Searches%,-1,0) DO (
CALL SET StringSegment=%%String:~%%n,!Substring_Length!%%
IF "%~2" EQU "!StringSegment!" SET /A Instance+=1
IF !Instance! EQU 0 SET Index=%%n & GOTO strIndex_end
))
:strIndex_end
EXIT /B %Index%
I'm trying to write a Windows batch file that will generate a random number from 0 to 9 and then load a different map list in our game server based on the random number.
I have tried to modify a similar file that was on this forum for generating random characters, but when I reduce the maxchars variables to 1 in length.
I sometimes get #echo is off as a response and sometimes get a number.
Here's what I have:
#echo off & setlocal EnableDelayedExpansion
REM Random.bat
REM
REM Change these values to whatever you want, or change the code to take them
REM as command-line arguments. You must set CHARS_LEN to the string length
REM of the string in the CHARS variable.
REM
REM This script generates a string of these characters at least
REM MIN_CHARS_IN_LINE chars long and at most MAX_CHARS_IN_LINE chars long.
SET CHARS=0123456789
SET /A CHARS_LEN=10 + 10 + 10
SET /A MIN_CHARS_IN_LINE=1
SET /A MAX_CHARS_IN_LINE=2
REM Pick a random line length and output a random character until we reach that
REM length.
call:rand %MIN_CHARS_IN_LINE% %MAX_CHARS_IN_LINE%
SET /A LINE_LENGTH=%RAND_NUM%
SET LINE=
for /L %%a in (1 1 %LINE_LENGTH%) do (
call:rand 1 %CHARS_LEN%
SET /A CHAR_INDEX=!RAND_NUM! - 1
CALL SET EXTRACTED_CHAR=%%CHARS:~!CHAR_INDEX!,1%%
SET LINE=!LINE!!EXTRACTED_CHAR!
)
echo !LINE!
goto:EOF
REM The script ends at the above goto:EOF. The following are functions.
REM rand()
REM Input: %1 is min, %2 is max.
REM Output: RAND_NUM is set to a random number from min through max.
:rand
SET /A RAND_NUM=%RANDOM% * (%2 - %1 + 1) / 32768 + %1
goto:EOF
:eof
Once I can get this reliably choosing characters I just need to add a selection process to the end which will call the server with a different command line for each map list.
The LastStar007 method works great for values between 0 and 9. For a more generic solution that returns a pseudo random number between 0 and n, simply use SET /A to get %random% modulo (n+1).
For example, 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 the modulo operator must be doubled
set /a "rand=%random% %% 10"
Use the random string in SET and then lop off everything but the last character.
SET RAND=%RANDOM:~-1%
If your problem still persists, try the following. It generates a number between a and b. Tweak it to your needs:
#echo off
color 02
echo enter value of A
set /p a=
echo.
echo enter value of B
set /p b=
:main
set no=%random%
if %no% GEQ %a% goto sub
if not %no% GEQ %a% goto main
:sub
if %no% LEQ %b% goto end
if not %no% LEQ %b% goto main
:end
echo %no%
goto main