2

我有一个bat文件

@echo %RANDOM%

并使用命令行执行它

start randomcheck.bat & start randomcheck.bat

打开了两个控制台,并且都包含相同的数字 4645。这无法实现随机提供不同临时文件夹的目的(我仅在同时使用它们时才需要随机文件夹)。你如何批量拥有正常的随机生成器?

更新 https://stackoverflow.com/a/19697361/1083704已经量化了全球种子更新周期。windows shell 全局种子每秒更新一次。实际上,我必须再增加一秒钟的安全裕度,以防止比赛,并希望这是足够的措施。这真的很糟糕。这意味着为我的 iCore7 启动 8 个进程将需要 16 秒。而且我仍然不确定这是否成功,因为没有任何正式规定,尽管最初有 8 个进程以相对时间偏移开始,但可能会发生两个进程同时完成,我必须注意它们是没有再次同时重新启动。这完全是胡说八道,我的问题是这可以批量治愈,而无需求助于 C++ 或 VBScript 吗?

4

6 回答 6

11

MC ND 在他的答案以及他的后续评论中的所有方面都是 100% 正确的。

CMD.EXE 的每个实例在启动时使用从当前时间派生的种子以 1 秒的分辨率初始化随机数生成器。在同一秒内启动的所有 CMD.EXE 进程将获得相同的随机数序列。

另一个方面 - 连续秒的初始随机数变化非常缓慢。在我看来,初始随机数实际上可能是从时间派生的种子值,但我不确定。

编辑 - 我最初是通过实验推断出所有这些。但后来我看到了权威人士的证实

这是一个脚本,演示了 CMD.EXE 的种子如何每秒仅更改一次,并且种子更改非常缓慢:

@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

-- 输出 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

此脚本演示随机数生成器在单个 CMD.EXE 进程中“正常”工作。

@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

-- 输出 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

最后,此脚本演示%random%了给定行中的每个行如何扩展为自己的值,但行值在循环迭代之间不会改变,因为循环行只解析一次。

@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

-- 输出 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
于 2013-10-31T02:22:04.940 回答
6

cmd 中的随机数生成器使用当前时间(秒分辨率)来播种 prng。因此,在同一秒开始的两个进程将生成相同的“随机”数。

对于不会发生冲突的选项,请使用 vbscript 的随机数生成器(先随机化),或使用 guid(您可以通过 uuidgen 或使用 vbscript 生成它),或 powershell,或 ....

于 2013-10-30T21:57:20.300 回答
5

混合批处理 Jscript 解决方案

@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));
于 2013-10-31T13:45:47.603 回答
4

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.

于 2013-11-13T00:31:09.530 回答
2

您甚至可以在一秒钟内启动 8 个任务,并且每个任务都有自己的随机值。

随机数由主任务生成并作为参数发送。

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

如果你的 task.bat 中还需要一个独立的随机生成器,你可以使用参数作为种子前缀。

任务.bat

setlocal EnableDelayedExpansion
set seed=%1
for /L %%n in ( 1 1 %seed%) do set dummy=!random!
于 2013-10-31T10:20:42.027 回答
0

$RANDOM在 bash 中没有这个缺陷。

@echo win=%RANDOM%

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

@echo cygwin=%VAR%

运行时打印相同的 Windows 编号但不同的 cygwin 编号start randomcheck.bat & start randomcheck.bat。使用 cygwin 比为单个命令编写单独的 VBScript 更好。可能你可以教我如何bash -c "echo $RANDOM"用 windows 脚本主机替代替换,这将消除安装 cygwin 的需要。

于 2013-10-31T11:18:36.210 回答