Arquivo em lote do Windows 7: Por que esses blocos IF impedem que eu atribua variáveis a um valor?

6

Aqui está o arquivo de lote quebrado:

@echo off
if prod==prod (
    if xps==xps (
        set i1=prodxpsi1
        set i2=prodxpsi2
        set e1=prodxpse1
        set e2=prodxpse2
    ) else (
        set i1=prodzpsi1
        set i2=prodzpsi2
        set e1=prodzpse1
        set e2=prodzpse2
    )

    if 1==1 (
        echo %i1%, %i2%, %e1%, %e2%
    ) else (
        echo %i1%, %i2%, %e1%, %e2%
    )
)

pause

No entanto, quando eu tiro o bloco externo if prod==prod desse jeito, funciona:

@echo off
if xps==xps (
    set i1=prodxpsi1
    set i2=prodxpsi2
    set e1=prodxpse1
    set e2=prodxpse2
) else (
    set i1=prodzpsi1
    set i2=prodzpsi2
    set e1=prodzpse1
    set e2=prodzpse2
)

if 1==1 (
    echo %i1%, %i2%, %e1%, %e2%
) else (
    echo %i1%, %i2%, %e1%, %e2%
)

pause

Quando executo o arquivo em lotes pela primeira vez, ele ecoa , , , . Quando eu corro pela segunda vez, funciona bem:

    
por oscilatingcretin 31.08.2011 / 05:07

4 respostas

5

Esta é uma peculiaridade do analisador de comandos. Por causa dos colchetes, ele vê tudo de if ... a ) como uma linha. Quando ele lê essa "linha única", ela expande todas as variáveis para seus valores antes de processar qualquer uma delas. Todos os comandos set ocorrem após as variáveis terem sido expandidas.

Existem duas soluções: ramificações e exsudação atrasada.

Ramos: Certifique-se de que os comandos set e echo não estão no mesmo conjunto de colchetes mais importantes:

@echo off
if not prod==prod goto :end
if xps==xps (
    set ...
) else (
    set ...
)
if 1==1 (
    ...
)
:end
pause

Expansão atrasada: Isso faz com que as variáveis sejam expandidas conforme necessário, em vez de antecipadamente. Use o comando SetLocal EnableDelayedExpansion para ativar este modo, use! marca para se referir a uma variável dessa maneira, e use o comando EndLocal quando estiver pronto. Note que EndLocal esquecerá qualquer variável declarada após SetLocal , então você pode querer mover SetLocal para depois dos comandos set .

@echo off
setlocal enabledelayedexpansion
if prod==prod (
    if xps==xps (
        set i1=prodxpsi1
        ...
    ) else (
        set i1=prodzpsi1
        ...
    )
    if 1==1 (
        echo !i1!, !i2!, !e1!, !e2!
    ) else (
        echo !i1!, !i2!, !e1!, !e2!
    )
)
endlocal
pause
    
por 31.08.2011 / 05:56
3

Se você ativar a expansão atrasada, use a sintaxe !var! para fazer referência às variáveis e, em seguida, ela se comportará conforme o esperado.

@echo off

setlocal enabledelayedexpansion

if prod==prod (
    if xps==xps (
        set i1=prodxpsi1
        set i2=prodxpsi2
        set e1=prodxpse1
        set e2=prodxpse2
    ) else (
        set i1=prodzpsi1
        set i2=prodzpsi2
        set e1=prodzpse1
        set e2=prodzpse2
    )

    if 1==1 (
        echo !i1!, !i2!, !e1!, !e2!
    ) else (
        echo !i1!, !i2!, !e1!, !e2!
    )
)

O texto de ajuda ( cmd /? ) explica:

/V:ON -- Enable delayed environment variable expansion using ! as the delimiter. For example, /V:ON would allow !var! to expand the variable var at execution time. The var syntax expands variables at input time, which is quite a different thing when inside of a FOR loop.

If delayed environment variable expansion is enabled, then the exclamation character can be used to substitute the value of an environment variable at execution time.

Quando você quebra a coisa toda em uma instrução if , o bloco inteiro se torna essencialmente um único comando. Todas as variáveis dentro do comando são expandidas no momento da entrada, ou seja, antes que a parte if xps==xps comece. Nesse ponto do script, as variáveis ainda não foram definidas.

Ao usar a sintaxe !var! junto com a expansão atrasada, o valor de !i1! não é avaliado até que essa linha específica seja executada.

Obrigado! Sua pergunta me ensinou algo hoje!

    
por 31.08.2011 / 05:53
0

todo o cmd stuff funciona mais como um pré-processador que executa o arquivo uma vez, cria o arquivo 'real' pré-processado e o executa. isso significa que cmd não (re) avalia variáveis dinamicamente definidas (ou for loops com base na mudança de conteúdo de uma 'variável'.

desde que sua descrição do problema é um pouco vaga: você usa variáveis em vez das strings 'prod' e 'xps' e espera uma interpretação dinâmica dessas 'variáveis'?

    
por 31.08.2011 / 05:36
0

Os programas a seguir são ilustrações simples do uso, dentro de uma instrução IF, do valor de uma variável definida dentro da mesma instrução. Eles são destinados a exibir "aa" se nenhum parâmetro for inserido e "bb" se um parâmetro for inserido. O exemplo 1 exibe apenas "a" se não houver nenhum parâmetro. Os outros exemplos funcionam corretamente.

Na minha opinião, o Exemplo 2, que usa "goto's", é o mais direto e fácil de entender.

Os exemplos 2, 5 e 6 funcionam retirando o código problemático da instrução IF.

@echo off & setlocal enableextensions & :: 1
if "%1" equ "" (
   set x=a
   set y=%x%
   ) else (
   set x=b
   set y=b
   )
echo %x%%y%
@echo off & setlocal enableextensions & :: 2
if "%1" neq "" goto 10
   set x=a
   set y=%x%
   goto 20
   :10
   set x=b
   set y=b
:20
echo %x%%y%
@echo off & setlocal enableextensions & :: 3
setlocal enabledelayedexpansion
if "%1" equ "" (
   set x=a
   set y=!x!
   ) else (
   set x=b
   set y=b
   )
endlocal & set "x=%x%" & set "y=%y%"
echo %x%%y%

No exemplo a seguir, observe o "setlocal" no bloco "else". Isso corresponde ao "endlocal", de modo que as variáveis "x" e "y" permaneçam locais no programa. (Caso contrário, o "endlocal" cancela o "setlocal" na primeira linha.)

@echo off & setlocal enableextensions & :: 4
if "%1" equ "" (
   set x=a
   setlocal enabledelayedexpansion
   set y=!x!
   ) else (
   set x=b
   set y=b
   setlocal
   )
endlocal &  set "y=%y%"
echo %x%%y%
@echo off & setlocal enableextensions & :: 5
if "%1" equ "" (
   call :seta
   ) else (
   call :setb
   )
echo %x%%y%
exit /b
:seta
   set x=a
   set y=%x%
   exit/b
:setb
   set x=b
   set y=b
   exit/b
@echo off & setlocal enableextensions & :: 6
set x=a
if "%1" equ "" (
   set y=%x%
   ) else (
   set x=b
   set y=b
   )
echo %x%%y%
    
por 08.07.2016 / 11:31