2013-06-21 4 views
11

Когда я пытаюсь использовать SHIFT внутри блока IF Я вижу неожиданные результаты. С помощью этого:Окно пакетного сдвига не работает, если блок

@echo off 

if "%1"=="/p" (
    echo %1 
    shift 
    echo "shifted" 
    echo %1 
) 

я получаю следующее:

C:\>ex.bat /p HAI 
/p 
"shifted" 
/p 

Однако, когда я использую этот код:

@echo off 

echo %1 
shift 
echo "shifted" 
echo %1 

Я получаю это:

C:\>ex.bat /p HAI 
/p 
"shifted" 
HAI 

мне нужно второй вывод, но в логическом блоке, поэтому я могу зацикливать ove Это так. Я пытаюсь реализовать что-то похожее на ответ Джона здесь: Using parameters in batch files at DOS command line, но у меня проблемы. Почему это происходит?

+0

В итоге я нашел ответ здесь: [Shift не работает в пакетном скрипте] (http://social.technet.microsoft.com/Forums/scriptcenter/en-US/7bbbe8df-e3c0-46ab-aede-396c2b6f6184/shift-doesnt-work-in-batch-script) – piebie

ответ

10

Это ожидаемое поведение, поскольку %1 разворачивается при анализе строки, и вся конструкция IF с конечным выражением анализируется за один проход. Таким образом, вы не можете видеть результат SHIFT до тех пор, пока не будет проанализирована новая строка, которая не произойдет до тех пор, пока вы не оставите свой скоброванный блок.

Та же проблема возникает при расширении переменных окружения с использованием %var%. С переменными окружения вы можете обойти эту проблему, включив замедленное расширение, используя SETLOCAL EnableDelayedExpansion, а затем используя !var!. Но нет сопоставимого способа расширения параметров с использованием замедленного расширения.

EDIT 2013-06-21 - Существует не сопоставимый способ, но есть простой способ, относительно медленный.

Вы можете принудительно перенаправить линию, используя CALL и удвоить %.

@echo off 

if "%1"=="/p" (
    echo %1 
    shift 
    echo "shifted" 
    call echo %%1 
) 



EDIT 2016-07-15 code in Vladislav's answer это плохая практика, поскольку это означает, что вы можете вернуться назад в пределах блока кода после того, как вы использовали GOTO, чтобы оставить его. Это просто не работает. GOTO немедленно убивает любые разборные кодовые блоки. Вы могли бы также написали код Владислава как:

@echo off 
if "%1"=="/p" (
    goto :true 
    :true 
    echo "%1" 
    shift 
    echo shifted 
    echo "%1" 
) 

Если условие ложно, то весь блок будет пропущен. Если условие TRUE, GOTO немедленно убивает блок, а затем выполнение обычно поднимается на метке: true (без контекста блока). Дополнительный ) в конце просто игнорируется.

Обратите внимание, что вы не можете добавить ELSE с этой конструкцией. Следующие результаты не дают желаемого результата:

@echo off 
if "%1"=="/p" (
    goto :true 
    :true 
    echo "%1" 
    shift 
    echo shifted 
    echo "%1" 
) else (
    echo This will execute regardless whether arg1 is /p or not 
) 

Если FALSE, то выполняется только блок ELSE. если TRUE, то верхний блок выполняется и сразу же убивается. Остальная часть кода выполняется, а строка ) else ( игнорируется, потому что ) выполняет функции как REM, если синтаксический анализатор ожидает команду, и в стеке нет активных скобок.

Я настоятельно рекомендую вам никогда не использовать GOTO метку в скобках кода в скобках, как я показал выше (или, как сделал Владислав).

Ниже лучше (проще и не вводит в заблуждении) способ сделать то же самое:

@echo off 
if not "%1"=="/p" goto :skip 
echo "%1" 
shift 
echo shifted 
echo "%1" 

:skip 

Вы можете поддержать/Then/Else концепции IF с некоторыми дополнительными этикетками и GOTOS

@echo off 
if not "%1"=="/p" goto :notP 
echo "%1" 
shift 
echo shifted 
echo "%1" 
goto :endIf 

:notP 
echo not /p 

:endIf 
2

Фактически вы можете просто выполнить сдвиг снаружи, если блок:

@echo off 
if "%1"=="/p" (
    echo "%1" 
    goto:perform_shift; 
:aftersift 
    echo "%1" 
) 
goto:end; 

:perform_shift 
shift 
echo "shifted" 
goto:aftersift; 


:end 
+1

Этот «вид работ», но это очень плохая форма. Это означает, что вы прыгаете в скобках, которые не работают. Когда вы используете GOTO внутри блока, вы убиваете блок; круглые скобки есть, но они не имеют никакой цели. Это не важно в этом случае, но очень важно, если блок является частью цикла FOR. Если вы GOTO в цикле FOR, тогда цикл будет убит. – dbenham

+0

см. [EDIT 2016-07-15 в своем ответе] (http://stackoverflow.com/a/17241649/1012053) для получения дополнительной информации о том, почему этот ответ не является хорошей идеей, а также предположение о лучшем способ эффективно делать то же самое. – dbenham