Random generator in the batch

前端 未结 6 1961
走了就别回头了
走了就别回头了 2020-11-27 23:20

I have a bat file

@echo %RANDOM%

and execute it using command line

start randomcheck.bat & start randomcheck.bat


        
相关标签:
6条回答
  • 2020-11-27 23:34

    You could start 8 tasks even in one second and each get it's own random value.

    The random number is generated by the main-task and send as parameter.

    setlocal EnableDelayedExpansion
    for /L %%n in (1 1 8) DO start task.bat !random!
    

    If you need in your task.bat also an independent random generator, you could use the parameter as seed prefix like.

    task.bat

    setlocal EnableDelayedExpansion
    set seed=%1
    for /L %%n in ( 1 1 %seed%) do set dummy=!random!
    
    0 讨论(0)
  • 2020-11-27 23:37

    Random number generator in cmd uses the current time (second resolution) to seed the prng. So, two processes starting in the same second will generate the same "random" number.

    For an option that will not collide, use the random number generator of vbscript (randomize first), or use a guid (you can generate it via uuidgen or also with vbscript), or powershell, or ....

    0 讨论(0)
  • 2020-11-27 23:44

    $RANDOM in bash does not have this flaw.

    @echo win=%RANDOM%
    
    @for /f %%i in ('bash -c "echo $RANDOM"') do @set VAR=%%i
    
    @echo cygwin=%VAR%
    

    prints the same windows numbers but distinct cygwin ones when I run start randomcheck.bat & start randomcheck.bat. Using cygwin is better than writing a separate VBScript for a single command. Might be you can teach me how to replace the bash -c "echo $RANDOM" with the windows scripting host alternative, which would eliminate the need for installing the cygwin.

    0 讨论(0)
  • 2020-11-27 23:46

    A hybrid batch-Jscript solution

    @set @e=0 /*
     @echo off
       set @e=
       cscript //nologo //e:jscript "%~f0"
     exit /b
    */
    
    function getRandomNumber(min, max) {
     return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    
    WScript.echo(getRandomNumber(0, 10000));
    
    0 讨论(0)
  • 2020-11-27 23:52

    MC ND is 100% correct on all counts within his answer, and also his follow-on comment.

    Each instance of CMD.EXE initializes the random number generator upon startup using a seed that is derived from the current time with 1 second resolution. All CMD.EXE processes that launch within the same second will get identical random number sequences.

    One additional aspect - the initial random number for consecutive seconds changes very slowly. It looks to me like the initial random number may actually be the seed value that was derived from the time, but I'm not sure.

    EDIT - I had originally deduced all of this via experimentation. But I have since seen confirmation from an authoritative source.

    Here is a script that demonstrates how the seed for CMD.EXE changes only once per second, and the seed changes very slowly:

    @echo off
    setlocal
    set "last=%time:~9,1%"
    for /l %%N in (1 1 30) do (
      call :wait
      cmd /c echo %%time%% %%random%% %%random%% %%random%% %%random%% %%random%% %%random%%
    )
    exit /b
    
    :wait
    if %time:~9,1% equ %last% goto :wait
    set "last=%time:~9,1%"
    exit /b
    

    -- OUTPUT 1 --

    22:13:26.31 30024 16831 1561 8633 8959 14378
    22:13:26.41 30024 16831 1561 8633 8959 14378
    22:13:26.51 30024 16831 1561 8633 8959 14378
    22:13:26.61 30024 16831 1561 8633 8959 14378
    22:13:26.71 30024 16831 1561 8633 8959 14378
    22:13:26.81 30024 16831 1561 8633 8959 14378
    22:13:26.91 30024 16831 1561 8633 8959 14378
    22:13:27.01 30027 27580 19425 32697 19274 18304
    22:13:27.11 30027 27580 19425 32697 19274 18304
    22:13:27.21 30027 27580 19425 32697 19274 18304
    22:13:27.31 30027 27580 19425 32697 19274 18304
    22:13:27.41 30027 27580 19425 32697 19274 18304
    22:13:27.51 30027 27580 19425 32697 19274 18304
    22:13:27.61 30027 27580 19425 32697 19274 18304
    22:13:27.71 30027 27580 19425 32697 19274 18304
    22:13:27.81 30027 27580 19425 32697 19274 18304
    22:13:27.91 30027 27580 19425 32697 19274 18304
    22:13:28.01 30030 5560 4521 23992 29588 22231
    22:13:28.11 30030 5560 4521 23992 29588 22231
    22:13:28.21 30030 5560 4521 23992 29588 22231
    22:13:28.31 30030 5560 4521 23992 29588 22231
    22:13:28.41 30030 5560 4521 23992 29588 22231
    22:13:28.51 30030 5560 4521 23992 29588 22231
    22:13:28.61 30030 5560 4521 23992 29588 22231
    22:13:28.71 30030 5560 4521 23992 29588 22231
    22:13:28.81 30030 5560 4521 23992 29588 22231
    22:13:28.91 30030 5560 4521 23992 29588 22231
    22:13:29.01 30033 16308 22385 15287 7135 26158
    22:13:29.11 30033 16308 22385 15287 7135 26158
    22:13:29.21 30033 16308 22385 15287 7135 26158
    

    This script demonstrates that the random number generator works "properly" within a single CMD.EXE process.

    @echo off
    setlocal enableDelayedExpansion
    set "last=%time:~9,1%"
    for /l %%N in (1 1 30) do (
      call :wait
      echo !time! !random! !random! !random! !random! !random! !random!
    )
    exit /b
    
    :wait
    if %time:~9,1% equ %last% goto :wait
    set "last=%time:~9,1%"
    exit /b
    

    -- OUTPUT 2 --

    22:16:10.30 24175 26795 4467 2450 12031 9676
    22:16:10.40 6873 17221 14201 17898 32541 29918
    22:16:10.50 700 21044 25922 8616 24057 7657
    22:16:10.60 25370 6519 26054 28443 4865 1931
    22:16:10.70 26989 9396 12747 26808 6282 32182
    22:16:10.80 22778 11460 11989 26055 10548 1809
    22:16:10.90 4668 27372 30965 12923 5941 16533
    22:16:11.00 23426 11396 24402 29658 5150 11183
    22:16:11.10 1557 13572 18815 21801 4103 23119
    22:16:11.20 3459 30126 20484 32750 3360 16811
    22:16:11.30 14041 26960 31897 24736 16657 1954
    22:16:11.40 5112 18377 30475 18837 12216 10237
    22:16:11.50 13136 6241 27074 29398 8996 9738
    22:16:11.60 16027 15122 13659 28897 4827 29753
    22:16:11.70 27502 8271 11489 21888 16590 7886
    22:16:11.80 30405 25582 7288 5432 7310 26557
    22:16:11.90 202 11076 23205 20739 28053 12621
    22:16:12.00 4234 20370 10355 5974 27590 8732
    22:16:12.10 24411 21836 16161 24731 22898 10378
    22:16:12.20 23060 17903 10788 19107 29825 15561
    22:16:12.30 6772 1371 674 13257 15504 18422
    22:16:12.40 1344 31971 23977 8630 10789 15367
    22:16:12.50 18945 17823 20691 10497 5958 31613
    22:16:12.60 18294 10398 26910 8744 21528 272
    22:16:12.70 25603 9991 24084 11667 16977 5843
    22:16:12.80 19405 5457 16285 11165 26783 10627
    22:16:12.90 20041 31763 26390 11994 19285 12287
    22:16:13.00 21342 13853 9336 24080 2555 2067
    22:16:13.10 9328 30429 1722 2211 22934 24871
    22:16:13.20 8168 21818 19125 11102 449 8813
    

    Finally, this script demonstrates how each %random% within a given line expands to its own value, but the line values don't change between loop iterations because the looped line is only parsed once.

    @echo off
    setlocal
    set "last=%time:~9,1%"
    for /l %%N in (1 1 30) do (
      call :wait
      echo %time% %random% %random% %random% %random% %random% %random%
    )
    exit /b
    
    :wait
    if %time:~9,1% equ %last% goto :wait
    set "last=%time:~9,1%"
    exit /b
    

    -- OUTPUT 3 --

    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    22:20:10.98 28188 30311 32299 7392 5874 32157
    
    0 讨论(0)
  • 2020-11-28 00:01

    Even a perfect random number generator can (will eventually) generate collisions. A robust solution must assume collisions are possible, and compensate accordingly.

    Here is one strategy that I have successfully used in the past:

    Assign the unique temp file name at the top of your script. Use the %TIME% value for a pseudo "random" number. Replace the : with nothing to make the string valid for a file name. Two processes will only collide if they start within 1/100 seconds of each other, (assuming your processes do not run for more than a day).

    You might get collisions. Collisions can be detected via a temporary lock file. Put the main body of your script in a subroutine, and call the main routine with a non-standard file handle redirected to a lock file with the "random" number - only one process can redirect output to the lock file at any given time. If a lock is detected, simply loop back and try again.

    @echo off
    setlocal
    
    :getUnique
    
    :: Derive a pseudo "unique" name from script name and current time
    set "tempBase=%temp%\%~nx0.%time::=%"
    
    :: If you want to test the lock mechanism, uncomment the following
    :: line which removes the time component from the "unique" name
    ::set "tempBase=%temp%\%~nx0.notUnique"
    
    :: Save stderr, then redirect stderr to null
    3>&2 2>nul (
    
      %= Establish lock =%
      9>"%tempBase%.lock" (
    
        %= Restore stderr and call main routine =%
        2>&3 (call :main %*)
    
        %= Capture the returned errorlevel if necessary =%
        call set "err=%%errorlevel%%
    
        %= Force ERRORLEVEL to 0 so that any error detected outside =%
        %= this block must be due to lock failure                   =%
        (call )
    
      %= Loop back and try again if lock failed due to collision =%
      ) || goto :getUnique
    )
    
    :: Delete the temp files and exit with the saved errorlevel
    del "%tempBase%*"
    exit /b %err%
    
    
    :main
    :: The rest of the script goes here.
    :: Additional unique temp file names can be derived from %tempBase% as needed.
    
    :: For this demo, I'll just list the temp file(s) and pause
    dir /b "%tempBase%*"
    pause
    
    :: Exit with an error for testing purposes
    exit /b 1
    

    It is unlikely that two processes will get the same unique name, but if they do, the second process will detect the collision, loop back and try again until it succeeds.

    Uncomment the nonUnique tempBase line if you want to test the locking. Open two console windows, and launch the script in both windows. The first will successfully enter the main routine and pause. The second will loop, waiting for the first to finish. Press a key on the first, and the first will instantly end and the second will continue into the main routine.

    If you want greater than 1/100 second precision, or if your processes may run longer than a day, then you should consider using WMIC OS GET LOCALDATETIME to get a string that includes date and time to 1/1000 second.

    0 讨论(0)
提交回复
热议问题