2

Как заставить этот скрипт работать в командной строке ?, сейчас все случайные числа совпадают

SET SOME[1]="AA"
SET SOME[2]="BB"
SET SOME[3]="CC"
SET SOME[4]="DD"
SET SOME[5]="EE"
FOR /L %%i IN (1,1,5) DO FOR /F %%j IN ('SET RND=%%RANDOM%%*5/32768+1') DO ECHO SOME[%%i] %%j

3 ответа3

7

Ух ты - у тебя много проблем с этим небольшим фрагментом кода :-)

В вашем опубликованном коде отсутствует опция SET /A. Я предполагаю, что ваш реальный код имеет это.

Причина, по которой ваш код завершается с ошибкой синтаксиса, заключается в том, что команда в предложении FOR IN() выполняется с помощью команды cmd /C yourCommandHere . Когда неявная команда cmd /C анализируется, она обрабатывает = как разделитель токена, если она не экранирована или не заключена в кавычки. Любая последовательная строка разделителей токенов преобразуется в один <space> перед тем, как ваша команда будет фактически выполнена в новом потоке CMD с использованием семантики командной строки. Список лексем , = <space> <non-breaking space> и <tab> .

Таким образом, цитирование команды устранит синтаксическую ошибку:

FOR /L %%i IN (1,1,5) DO FOR /F %%j IN ('SET /A "RND=%%RANDOM%%*5/32768+1"') DO ECHO SOME[%%i] %%j

Как будет сбегать =:

FOR /L %%i IN (1,1,5) DO FOR /F %%j IN ('SET /A RND^=%%RANDOM%%*5/32768+1') DO ECHO SOME[%%i] %%j

Но вам не нужно присваивать случайное число переменной. Команда FOR IN() выполняется в контексте командной строки, а SET /A выводит вычисленное значение в стандартный вывод при запуске в контексте командной строки. Таким образом, следующее также устраняет любую синтаксическую ошибку с фактически теми же результатами:

FOR /L %%i IN (1,1,5) DO FOR /F %%j IN ('SET /A %%RANDOM%%*5/32768+1') DO ECHO SOME[%%i] %%j

Вот более простой способ получить результат от 1 до 5 (случайный мод 5 + 1):

FOR /L %%i IN (1,1,5) DO FOR /F %%j IN ('SET /A %%RANDOM%% %% 5 + 1') DO ECHO SOME[%%i] %%j

Но я серьезно сомневаюсь, что любое из вышеперечисленных исправлений даст желаемый результат.

Что-то очень странное происходит со значением %RANDOM% . Использование %%RANDOM%% правильно заставляет выражение вычисляться каждую итерацию. Но по какой-то причине случайное число является почти постоянным для любого заданного прогона. Время от времени одна из итераций будет меняться, но для большинства прогонов каждая итерация получает постоянное значение. Я думаю, что это как-то связано с начальным значением генератора случайных чисел. Возможно, генератор случайных чисел запускается с начальным значением каждый раз, когда начинается сеанс CMD, и начальное значение изменяется только очень медленно. Помните, что предложение FOR IN() выполняется в новом сеансе CMD.

Вот тестовая программа, которая демонстрирует, что %%test%% правильно переоценивается на каждой итерации. Это также показывает, как %%random%% остается почти постоянным в пределах цикла.

@echo off
setlocal
set test=0
for /l %%I in (1 1 5) do for /f "delims=" %%J in ('echo %%test%% %%random%%') do (
  echo %%J
  set "test=%%I
)

Вот вывод из 2 запусков приведенного выше кода. Обратите внимание, что 1-й прогон имеет одну вариацию случайного числа. Второй прогон имеет постоянную случайную величину.

C:\test> test
0 20369
1 20373
2 20373
3 20373
4 20373

C:\test> test
0 20379
1 20379
2 20379
3 20379
4 20379

На самом деле нет смысла помещать вычисление случайных чисел в предложение FOR /F IN('command'). Все намного проще, если вы используете SET /A с отложенным расширением непосредственно в вашем внешнем цикле.

Я считаю, что вы можете искать следующее:

@echo off
setlocal enableDelayedExpansion
SET SOME[1]="AA"
SET SOME[2]="BB"
SET SOME[3]="CC"
SET SOME[4]="DD"
SET SOME[5]="EE"
FOR /L %%i IN (1,1,5) DO (
  set /a rand=!random!%%5+1
  for %%N in (!rand!) do echo %%i: SOME[%%N]=!SOME[%%N]!
)

Вот пример вывода:

C:\test>test
1: SOME[3]="CC"
2: SOME[5]="EE"
3: SOME[2]="BB"
4: SOME[2]="BB"
5: SOME[5]="EE"

РЕДАКТИРОВАТЬ

Вот лучшее доказательство того, что рандомизатор для сеанса CMD повторно заполнен, и начальное число изменяется только медленно.

@echo off
setlocal enableDelayedExpansion

echo Within a single CMD session, every ^^!random^^! gets a new value.
for /l %%N in (1 1 10) do call echo !time! !random! !random!

echo(

setlocal disableDelayedExpansion
echo But each CMD session !random! is reseeded,
echo and the seed only changes once per second,
echo and the inital value changes slowly:
for /l %%N in (1 1 30) do cmd /v:on /c "echo !time! !random! !random!&for /l %%A in (1 1 50000) do @rem"

--ВЫХОД--

Within a single CMD session, every !random! gets a new value.
11:12:10.37 17810 1733
11:12:10.37 8919 24464
11:12:10.37 9931 2137
11:12:10.37 28574 16630
11:12:10.37 30379 23234
11:12:10.37 22410 31740
11:12:10.38 15479 14080
11:12:10.38 812 23616
11:12:10.38 1384 25909
11:12:10.38 2733 1947

But each CMD session !random! is reseeded,
and the seed only changes once per second,
and the inital value changes slowly:
11:12:10.39 4552 6316
11:12:10.50 4552 6316
11:12:10.61 4552 6316
11:12:10.71 4552 6316
11:12:10.82 4552 6316
11:12:10.92 4552 6316
11:12:11.03 4555 17064
11:12:11.14 4555 17064
11:12:11.24 4555 17064
11:12:11.35 4555 17064
11:12:11.45 4555 17064
11:12:11.56 4555 17064
11:12:11.67 4555 17064
11:12:11.77 4555 17064
11:12:11.88 4555 17064
11:12:11.99 4555 17064
11:12:12.09 4559 27813
11:12:12.20 4559 27813
11:12:12.30 4559 27813
11:12:12.41 4559 27813
11:12:12.51 4559 27813
11:12:12.62 4559 27813
11:12:12.73 4559 27813
11:12:12.83 4559 27813
11:12:12.94 4559 27813
11:12:13.04 4562 5793
11:12:13.15 4562 5793
11:12:13.26 4562 5793
11:12:13.36 4562 5793
11:12:13.47 4562 5793
3

Поскольку вы расширяете переменную %RANDOM% в цикле FOR , вам нужно использовать отложенное расширение.

Это производит 5 эхо того же числа:

FOR /L %%i IN (1,1,5) DO (
echo %random%
)

Это производит 5 "случайных" чисел:

setlocal ENABLEDELAYEDEXPANSION

FOR /L %%i IN (1,1,5) DO (
echo !random!
)

Вы можете найти больше информации о отложенном расширении, запустив

SET /?
2

В этом случае вам нужно delayed expansion .

echo off & setlocal enabledelayedexpansion
set test=0
for /l %%I in (1 1 5) do for /f "delims=" %%J in ('echo !test! !random!') do (
  echo %%J
  set "test=%%I"
)

Это быстрее, чем %%test%% . Пример вывода:

0 26542
1 32475
2 25609
3 4495
4 6719

Это также работает (медленнее):

echo off & setlocal enabledelayedexpansion
set test=0
for /l %%I in (1 1 5) do for /f "delims=" %%J in ('cmd /v:on /c echo !test! !random!') do (
  echo %%J
  set "test=%%I"
)

Но следующее не работает:

echo off & setlocal
set test=0
for /l %%I in (1 1 5) do for /f "delims=" %%J in ('cmd /v:on /c echo !test! !random!') do (
  echo %%J
  set "test=%%I"
)

,

0 19919
1 19919
2 19919
3 19919
4 19919

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .