6

Вот сломанный пакетный файл:

@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

Однако, когда я вынимаю внешний блок if prod==prod следующим образом, он работает:

@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

При запуске пакетного файла в первый раз, это , , , Когда я запускаю его во второй раз, он работает нормально:

lolwut

4 ответа4

5

Это причуды анализатора команд. Из - за скобки, он видит все от того, if ... ) в одной строке. Когда он читает эту "одну строку", он расширяет все переменные до их значений перед обработкой любой из них. set команд все occcur после того , как переменные были расширены.

Есть два решения: ветки и отложенное расширение.

Ветви: Убедитесь, что команды set и echo не находятся в одном наборе самых верхних скобок:

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

Отложенное расширение: это вызывает расширение переменных по мере необходимости, а не заранее. Используйте команду SetLocal EnableDelayedExpansion чтобы активировать этот режим, используйте! помечает для ссылки на переменную таким образом и использует команду EndLocal когда вы закончите. Обратите внимание, что EndLocal забудет все переменные, объявленные после SetLocal , поэтому вы можете захотеть переместить SetLocal после 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
3

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

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

Текст справки (cmd /?) объясняет:

/V:ON - Включить отложенное расширение переменной среды с помощью! в качестве разделителя. Например, /V:ON позволит!вар! расширить переменную var во время выполнения. Синтаксис var расширяет переменные во время ввода, что совершенно иначе, когда внутри цикла FOR.

Если отложенное расширение переменной среды включено, то восклицательный знак можно использовать для замены значения переменной среды во время выполнения.

Когда вы заключаете все это в оператор if , весь блок становится по существу одной командой. Все переменные внутри команды раскрываются во время ввода, то есть до начала части if xps==xps . На данный момент в сценарии переменные еще не определены.

Используя !var! синтаксис вместе с отложенным расширением, то значение !i1! не оценивается, пока эта конкретная строка не будет выполнена.

Спасибо! Ваш вопрос научил меня чему-то сегодня!

0

весь cmd работает больше как препроцессор, который запускает файл один раз, создает предварительно обработанный «настоящий» файл и затем запускает его. это означает, что cmd не (повторно) оценивает динамически установленные переменные (или for циклов на основе изменяющегося содержимого «переменной»).

поскольку ваше описание проблемы немного расплывчато: вы используете переменные вместо строк «prod» и «xps» и ожидаете динамической интерпретации этих «переменных»?

0

Следующие программы являются простыми иллюстрациями использования в выражении IF значения переменной, установленной в том же выражении. Они предназначены для отображения «aa», если параметр не введен, и «bb», если параметр введен. В примере 1 отображается только «a», если нет параметров. Все остальные примеры работают правильно.

На мой взгляд, пример 2, в котором используется «goto», является наиболее простым и простым для понимания.

Примеры 2, 5 и 6 работают, убирая проблемный код из оператора 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%

В следующем примере обратите внимание на «setlocal» в блоке «else». Это соответствует 'endlocal', так что переменные 'x' и 'y' остаются локальными для программы. (В противном случае endlocal отменяет setlocal в первой строке.)

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

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