Смещение пакета Windows не работает, если блок

Когда я пытаюсь использовать 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

Мне нужен второй вывод, но в логическом блоке, чтобы я мог его перебрать. Я пытаюсь реализовать что-то похожее на Jon здесь: Использование параметров в пакетных файлах в командной строке DOS, но у меня проблемы. Почему это происходит?

Ответ 1

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

Такая же проблема возникает при расширении переменных среды с использованием %var%. С переменными окружения вы можете обойти проблему, включив замедленное расширение, используя SETLOCAL EnableDelayedExpansion а затем используя !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 Код во Владиславе отвечает плохой практикой, так как это означает, что вы можете вернуться в блок кода после того, как вы использовали GOTO, чтобы оставить его. Это просто не работает. GOTO немедленно убивает любые разборные кодовые блоки. Вы могли бы написать код Владислава как:

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

Если условие FALSE, то весь блок пропускается. Если условие 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

Вы могли бы поддержать концепцию IF/THEN/ELSE с некоторыми дополнительными ярлыками и GOTO

@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

Ответ 3

Коды переключения не имеют немедленного эффекта внутри блока, но снаружи.

Предполагая, что вы хотите читать параметры как пары ключевых значений:

:Parse
if "%1" equ "/p" (
    set P=%2
    shift
    shift
)
if "%1" equ "/q" (
    set Q=%2
    shift
    shift
)
if "%1" neq "" goto Parse
echo P: %P%
echo Q: %Q%