1

I'm trying to make a batch file that creates a serial number in the format xxxx-xxxx-xxxx-xxxx-xxxx - that is, I want five randomly-generated four-digit numbers.

Unfortunately, %random% can return numbers that are 5 digits, or 3 digits, or 2 digits, or even 1 digit. Is there any way to guarantee that the random number will have four digits in it?

Here is the code I have so far:

@echo off
goto a
:b
echo it's a 5 digits number!
:a
set "x=%random%-%random%-%random%-%random%-%random%"
if %random% gtr 9999 goto b
echo it worked
echo %x%
pause
SomethingDark
  • 13,229
  • 5
  • 50
  • 55
  • What does this code currently do? Your text suggests it currently generates a serial number consisting of 5 digit numbers, is that right? – GolezTrol Jul 28 '15 at 21:06
  • possible duplicate of [How to use random in BATCH script?](http://stackoverflow.com/questions/5777400/how-to-use-random-in-batch-script) –  Jul 28 '15 at 21:06
  • @Adam The first answer in the linked question tells you exactly how to do what you want –  Jul 28 '15 at 21:07
  • @HoboSapiens ive read that before, tried it, didnt work.... – AdamTheCarpenter Jul 28 '15 at 21:14
  • 1
    @Adam then you should indicate that in your question, and explain the problems with likely duplicates that you have tried. –  Jul 28 '15 at 21:51
  • I recommend to adapt the question title to be more specific than "need help"... – aschipfl Jul 28 '15 at 21:54

2 Answers2

1

With a small formula, you can change the range of the value of random (0 to 32768) to a smaller value. By making that calculation a subroutine, you can easily call it multiple times.

Using setlocal you prevent pollution of your environment. The script below fills the variable serial with a random serial number consisting of 5 numbers between 1000 and 9999.

Version 1:

@echo off
setlocal

:: Generate 5 random numbers of 4 digits
call :random code1
call :random code2
call :random code3
call :random code4
call :random code5

:: Combine them and show the output.
set serial=%code1%-%code2%-%code3%-%code4%-%code5%
echo %serial%
pause

:: Clear all variables, except %serial%
endlocal & set serial=%serial%
goto :eof

:random
:: Version 1: Generates a random number between 1000 and 9999
setlocal
set /a nr=%random%*8999/32767+1000
endlocal & set %1=%nr%
goto :eof

Version 2:

Since :random is a nice separate subroutine, you can easily change it if you need. For instance, if 1000 to 9999 isn't good enough, you can get the range of 0000 to 9999 and left-pad the generated numbers with zeroes by replacing the subroutine with:

:random
:: Version 2: Generates a random number between 0000 and 9999
setlocal
set /a nr=%random%*9999/32767
set nr=0000%nr%
set nr=%nr:~-4%
endlocal & set %1=%nr%

A breakdown of the code.

  • setlocal starts a new local environment. Environment variables are copied into this new environment, but if you change add or remove them, that doesn't affect the environment variables outside of this 'local' scope. See also endlocal.
  • :: as rem used for comments
  • :random is a label. Identifiers starting with a colon indicate a label. Labels can be used for control flow of the program. The code after this label I call a subroutine. You can jump to a label to execute the subroutine (call) and return from it (goto :eof) to continue normal execution. It's as if you are calling a separate batch file, only it is embedded in the same file.
  • call calls a batch file or subroutine. By specifying a label you jump to another part of the batch file, but unlike goto, call allows you to jump back to the calling position.
  • goto :eof jumps to the end of the script. If you called a label using call, this command jumps back to the place of the call and continue execution from there. So it's like returning from a function.
  • code1 to code5. I specify the name of the variable in which I want to have the result of the subroutine. That way, I can call it five times to fill five different variables.
  • endlocal ends the local scope started with startlocal.
  • endlocal & set serial=%serial%. This is a trick. The local scope is ended, but before it is ended (by executing the line), the command interpreter interprets the entire line, expanding variables. This allows you to set a variable in the outer (or global) scope to a value that was created in the inner scope. I basically use this to specify a return value from the subroutine, and to return the variable serial from the script.
  • endlocal & set %1=%nr% as used in the subroutine. This sets the variable of which the name was specified in the first parameter of the call. So %1 expands to code1, code2, etc. This way, the subroutine can set any variable without needing to know which one it is setting.
  • set /a sets a variable but allows simple arithmetic formulas (calculations). This way you can calculate a value between 1000 and 9999 (version 1) or 0 and 9999 (version 2).
  • In version 2, I pad the value with zeroes if it is shorter. This is done in three steps. First generate the number, then add zeroes in front of it, and lastly, grab the last four characters, using the notation %nr:~-4%.

And if all that is too complex for you, you can just copy the formula itself and skip all the rest:

Version 3:

@echo off
set /a code1=%random%*9999/32767
set code1=0000%code1%

set /a code2=%random%*9999/32767
set code2=0000%code2%

set /a code3=%random%*9999/32767
set code3=0000%code3%

set /a code4=%random%*9999/32767
set code4=0000%code4%

set /a code5=%random%*9999/32767
set code5=0000%code5%

set serial=%code1:~-4%-%code2:~-4%-%code3:~-4%-%code4:~-4%-%code5:~-4%
echo %serial%
pause

Version 4: Very very very simple version. Jahwi suggested using the modulo operation to limit the number, so using set /a nr=%random%%%10000 would give you a number between 0 and 9999. However, the division isn't really even. Since you get a random number between 0 and 32767, doing just this modulo operation will result in numbers between 0 and 2767 occurring more often than numbers between 2768 and 9999.

If this doesn't bother you, you can use this method, or just simply truncate the value on 4 digits, which essentially has the same effect. The code below just truncates the random number and makes sure they are left padded with zeroes until a length of 4. This is about as short as it gets, but keep in mind that these numbers are less 'pure' and balanced than the ones in versions 1 to 3.

@echo off
set code1=0000%random%
set code2=0000%random%
set code3=0000%random%
set code4=0000%random%
set code5=0000%random%

set serial=%code1:~-4%-%code2:~-4%-%code3:~-4%-%code4:~-4%-%code5:~-4%
echo %serial%
pause
GolezTrol
  • 114,394
  • 18
  • 182
  • 210
  • im sorry, the :: are like rem function? and the :random is like the %random%? never saw something like this :3 and whats the setlocal? – AdamTheCarpenter Jul 28 '15 at 21:19
  • it worked, but its too complicated, as I dont understand some of the functions as I elaborated above – AdamTheCarpenter Jul 28 '15 at 21:25
  • :: and rem start a comment. :random is a label. %random% is a variable. setlocal localizes variables so that they are exclusive. – RGuggisberg Jul 28 '15 at 21:32
  • Maybe you don't need all of it, but I think some of the details (some comments and the use of `setlocal` are the icing on the cake to make it a neat script. I've added an almost step-by-step description to tell you what it does. – GolezTrol Jul 28 '15 at 21:35
  • @GolezTrol thank you! can you explain :eof? is it like exit commad? – AdamTheCarpenter Jul 28 '15 at 21:36
  • Similar. I've added some more explanation regarding that and other parts. I think this should get you going. I've also added a simplified version at the bottom, although I personally don't like copying the formula 5 times, and neither the fact that it 'pollutes' the environment with all those variables. – GolezTrol Jul 28 '15 at 21:42
  • @GolezTrol I dont know how to thank you, I genuinely think you should be honored for your massive assistance ^_^ and you have quite an impressive profile....I am thankful =) – AdamTheCarpenter Jul 28 '15 at 21:50
  • 1
    @AdamTheCarpenter. please acquire a minimum set of knowledge before asking questions here. It seems you do not only have issues with %random% but with batch files in general – Thomas Weller Jul 28 '15 at 21:53
  • I added an even shorter version, but although it seems to work nice, I think theoretically the results are less 'pure' than with the other versions. Still, if brevity and simplicity is what you want... :) – GolezTrol Jul 28 '15 at 22:02
  • *"I dont know how to thank you,"* - You can upvote any answer that is helpful to you. Also you can accept the single answer that helped you the most and really answered your question. See [What should I do when someone answers my question?](http://stackoverflow.com/help/someone-answers). – GolezTrol Jul 28 '15 at 22:06
  • @ThomasWeller wow, thanks for pointing that out genius....I never said I am a professional, if I lack skills doesnt mean I cant enjoy scripting, and if you arent going to answer my question, you arent helping either,I can appreciate constructive criticism, but that isnt the case here, is it? – AdamTheCarpenter Jul 29 '15 at 00:52
0

The problem is, you're not setting limits to your random output. revised code:

@echo off 
:a
set /a no1=%random%%%10
set /a no2=%random%%%10
set /a no3=%random%%%10
set /a no4=%random%%%10
set /a no5=%random%%%10
set x=%no1%-%no2%-%no3%-%no4%-%no5%
echo %x% 
pause

notice the %random%%%9 this tells the batch program that each generated number must NOT be greater than 9. i.e random number must always be less than 10.

Jahwi
  • 426
  • 3
  • 14
  • Nice solution, if you use `10000` instead of `10` you will get a four digit number: `%random%%%10000`. However, the division isn't really even. Since you get a random number between 0 and 32767, doing just this modulo operation will result in numbers between 0 and 2767 occurring more often than numbers between 2768 and 9999. Also, the numbers aren't padded. – GolezTrol Jul 28 '15 at 21:53
  • @Jahwi thank you ^_^ I wish I had enough reputation to upvote you, seems I need 15 reputation, good job! – AdamTheCarpenter Jul 29 '15 at 00:53