O número aleatório em um arquivo de lote não muda

2

Como fazer este script funcionar no prompt de comando ?, agora todos os números aleatórios são os mesmos

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
    
por nerdwaller 27.04.2013 / 13:10

3 respostas

7

Uau - você tem muitos problemas com esse pequeno trecho de código: -)

Seu código postado está sem a opção SET / A. Eu estou supondo que o seu código real tem isso.

O motivo pelo qual seu código falha com um erro de sintaxe é porque o comando dentro da cláusula FOR IN () é executado por meio de um comando cmd /C yourCommandHere . Quando o comando implícito cmd /C é analisado, ele trata = como um delimitador de token, a menos que seja escapado ou citado. Qualquer sequência consecutiva de delimitadores de token é convertida em um único <space> antes de seu comando ser realmente executado em um novo encadeamento CMD usando a semântica da linha de comando. A lista de delimitadores de token é , ; = <space> <non-breaking space> e <tab> .

Então, citar o comando eliminará o erro de sintaxe:

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

Como vai escapar o = :

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

Mas você não precisa atribuir o número aleatório a uma variável. O comando FOR IN () é executado dentro de um contexto de linha de comando, e SET / A imprimirá o valor calculado para stdout quando executado dentro de um contexto de linha de comando. Portanto, o seguinte também elimina qualquer erro de sintaxe com os mesmos resultados:

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

Aqui está um método mais simples para dar um resultado de 1 a 5 (mod aleatório 5 + 1):

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

Mas duvido seriamente que qualquer uma das correções acima forneça o resultado desejado.

Há algo muito peculiar acontecendo com o valor de %RANDOM% . Seu uso de %%RANDOM%% faz com que a expressão seja avaliada a cada iteração. Mas por algum motivo, o número aleatório é quase constante para qualquer corrida. De vez em quando, uma das iterações varia, mas para a maioria das execuções, cada iteração recebe um valor constante. Eu acho que deve ter algo a ver com o valor inicial do gerador de números aleatórios. Talvez o gerador de números aleatórios seja iniciado com um valor inicial a cada vez que uma sessão CMD for iniciada, e o valor inicial só estará mudando muito lentamente. Lembre-se de que a cláusula FOR IN () é executada em uma nova sessão CMD.

Este é um programa de teste que demonstra que o %%test%% está sendo reavaliado corretamente a cada iteração. Também mostra como %%random%% permanece quase constante em uma execução.

@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
)

Aqui está a saída de 2 execuções do código acima. Observe como a primeira execução tem uma variação no número aleatório. A segunda corrida tem um valor aleatório constante.

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

Não há realmente nenhuma razão para colocar o cálculo do número aleatório dentro de uma cláusula FOR / F IN ('command'). Tudo é muito mais simples se você usar SET / A com expansão atrasada diretamente dentro de seu loop externo.

Acredito que o seguinte pode ser o que você está procurando:

@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]!
)

Aqui está um exemplo de saída:

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

EDITAR

Aqui está uma melhor evidência de que o randomizador para a sessão do CMD é propagado novamente, e a semente muda apenas lentamente.

@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"

- OUTPUT -

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
    
por 27.04.2013 / 17:49
3

Como você está expandindo a variável %RANDOM% em um loop FOR , é necessário usar a expansão atrasada.

Isso produz 5 ecos do mesmo número:

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

Isso produz 5 números "aleatórios":

setlocal ENABLEDELAYEDEXPANSION

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

Você pode encontrar mais informações sobre a expansão atrasada executando

SET /?
    
por 27.04.2013 / 14:41
2

Nesse caso, você precisa de 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"
)

Isso é mais rápido que %%test%% . Exemplo de saída:

0 26542
1 32475
2 25609
3 4495
4 6719

Isso também funciona (mais lento):

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"
)

Mas o seguinte não funciona:

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
    
por 27.04.2013 / 20:31