'Why is %random% in Windows time based and how to get a real random number?

I use the following script:

set /a num=%random% %%1000 +3000
timeout /T %num%
taskkill /F /IM chrome.exe

I trigger it using Windows task scheduler on multiple virtual machines at the exact same time.

The problem is: The value is not really random. It feels like it is Windows time based. I get the same value on every machine.



Solution 1:[1]

Use vbscript to get random number & enter your number in max and Min value :

@echo off
cd /d "%~dp0"
set /a max=3000
set /a min=1
(
Echo max=%max%:min=%min%:Randomize
Echo WSH.Echo ^(Int^(^(max-min+1^)^*Rnd+min^) + max^) ) >> Vbscript.vbs
FOR /F %%A IN ('cscript //nologo Vbscript.vbs') DO Set RAND=%%A
Del "Vbscript.vbs"
Echo %RAND%
timeout /T %RAND%
taskkill /F /IM chrome.exe
pause>nul

Solution 2:[2]

Use powershell to get the random numbers in a range:

@Echo Off
For /F %%a in ('Powershell -Command "& {[math]::Floor(Get-Random -Maximum 3000 -Minimum 1000)}"') Do Set rand=%%a
Echo %rand%
Timeout /T %rand%
Taskkill /F /Im chrome.exe
Pause >nul

Solution 3:[3]

Below is a pure batch solution without any VBS, JScript or PowerShell, but it's highly recommended to just avoid cmd with nasty legacy things and just use PowerShell. Things like this never happen on PowerShell

Why is %random% Windows time based and how to get a real random number?

%random% is a special variable in cmd.exe. It has nothing to do with Windows itself. It's seeded by the famous classic srand((unsigned)time(NULL));1 like most naive/simple C or C++ code so it can't provide proper random sequences if all of them start in the same second (which is the resolution of time() function). There are so many related issues:

The common solution is to use a high resolution time value like nanosecond because it's extremely unlikely for 2 processes to start in the same millisecond or nanosecond. That's still "time-based" but almost all random numbers are pseudo-random and seeded with time like that. There aren't any other good way to get a different seed that changes every time in cmd. You can never get a real random number in computers without a real entropy source

You can do a similar workaround by modifying the value with centisecond like this:

set /a "num=(random ^ %time:~-2%) %% 1000 + 3000"

Or get millisecond like this

for /F "usebackq tokens=2 delims==" %%i in (
    `wmic os get LocalDateTime /VALUE`
) do @set ctime=%%i
set /a "num=(random ^ %ctime:~15,3%) %% 1000 + 3000"

For better results some programs combine the time with the process ID so that no two processes have the same seed. You can also do that

for /f "usebackq tokens=2 delims==; " %%a in (
    `wmic process call create '"cmd.exe" "/c exit"' ^| find "ProcessId"`
) do @set PID=%%a
for /F "usebackq tokens=2 delims==" %%i in (
    `wmic os get LocalDateTime /VALUE`
) do @set ctime=%%i
set /a "num=(random ^ %ctime:~15,3%*31 ^ PID) %% 1000 + 3000"

Here a child's PID is used instead of the current PID, but it's the same for this purpose


1As random as I wanna be: Why cmd.exe's %RANDOM% isn't so random

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 phuclv
Solution 2 Wasif
Solution 3