Как работает отложенное расширение в пакетном скрипте?

Как работает отложенное расширение в пакетном скрипте?

Рассмотрим эту простую серию команд, выполняемых в пакетной среде:

>set ar[0]=orange
>set ar[1]=apple
>set ar[2]=banana
>for %i in (0,1,2) do echo !ar[%i]!

Выводит следующее:

>echo !ar[0]!
!ar[0]!
>!ar[1]!
!ar[1]!
>!ar[2]!
!ar[2]!

Так что это, очевидно, не расширяется до его значений. Как бы я это сделал?

решение1

Источники:
Фаза 5вКак интерпретатор команд Windows (CMD.EXE) анализирует скрипты?

Задержка расширения:Только если включено отложенное расширение, команда не находится вблок в скобках по обе стороны трубы, и команда не является«голый» пакетный скрипт(имя скрипта без скобок, CALL, конкатенация команд или конвейер).

  • Каждый токен команды анализируется на предмет отложенного расширения независимо.
    • Большинство команд анализируют два или более токенов — токен команды, токен аргументов и каждый токен назначения перенаправления.
    • Команда FOR анализирует только токен предложения IN.
    • Команда IF анализирует только сравниваемые значения — одно или два, в зависимости от оператора сравнения.
  • Для каждого проанализированного токена сначала проверьте, содержит ли он какой-либо !. Если нет, то токен не проанализирован — важно для ^символов. Если токен содержит !, то просканируйте каждый символ слева направо:
    • Если это символ вставки ( ^), то следующий символ не имеет особого значения, сам символ вставки удаляется.
    • Если это восклицательный знак, найдите следующий восклицательный знак (знаки препинания больше не наблюдаются), расширьте его до значения переменной.
      • Последовательные открытия !сворачиваются в одно!
      • Все оставшиеся непарные элементы !удаляются.
    • Расширение переменных на этом этапе «безопасно», поскольку специальные символы больше не обнаруживаются (даже <CR>или <LF>).
    • Для более полного объяснения прочтите вторую половину этого от dbenham та же тема - Фаза восклицательного знака

Фаза 5.3) Обработка труб:Только если команды находятся на обеих сторонах канала.
Каждая сторона канала обрабатывается независимо и асинхронно.

  • Если команда является внутренней для cmd.exe, или это пакетный файл, или если это заключенный в скобки блок команд, то он выполняется в новом потоке cmd.exe через %comspec% /S /D /c" commandBlock", поэтому блок команд получает фазовый перезапуск, но на этот раз в режиме командной строки.
    • Если это блок команд в скобках, то все <LF>, до и после которого есть команда, преобразуются в <space>&. Остальные <LF>удаляются.
  • На этом обработка команд конвейера завершена.
  • Видетьhttps://stackoverflow.com/q/8192318/1012053для получения дополнительной информации о разборе и обработке каналов

Фаза 5.5) Выполнение перенаправления:Теперь выполняется любое перенаправление, обнаруженное на этапе 2.



введите описание изображения здесь

введите описание изображения здесь

set ar[0]=orange
set ar[1]=apple
set ar[2]=banana
for %i in (0,1,2) do cmd.exe /v:on /C"echo !ar[%i]!

  • В вашей/файл без объявленияsetlocal enabledelayedexpansion, вы также можете использоватьcmd.exe /v:on /c "command & command | command || command..."
@echo off 

set "ar[0]=orange" 
set "ar[1]=apple"
set "ar[2]=banana" 
for %%i in (0,1,2)do cmd /v /c "echo\ !ar[%%i]!"

@echo off 

set "ar[0]=orange"
set "ar[1]=apple"
set "ar[2]=banana" 

setlocal enabledelayedexpansion
for %%i in (0,1,2)do echo\ !ar[%%i]!

enndlocal


@echo off 
set "ar[0]=orange" & set "ar[1]=apple" & set "ar[2]=banana" 
for %%i in (0,1,2)do %ComSpec% /v:on /c"echo !ar[%%i]!"
%__APPDIR__%timeout.exe /t -1 & endlocal & goto :EOF
@echo off 
set "ar[0]=orange" & set "ar[1]=apple" & set "ar[2]=banana" 
setlocal enabledelayedexpansion && for %%i in (0,1,2)do echo\ !ar[%%i]!
%__APPDIR__%timeout.exe /t -1 & endlocal & goto :EOF
@echo off 
set "ar[0]=orange" && set "ar[1]=apple" && set "ar[2]=banana" 
for %%i in (0,1,2)do <con: %ComSpec% /v:on /c"echo !ar[%%i]!"
call <con: rem./ && %__APPDIR__%timeout.exe /t -1 && endlocal
  • Вы также можете использоватьcallв/или в командной строке, чтобы обновить это значение:
set "ar[0]=orange"
set "ar[1]=apple"
set "ar[2]=banana" 
for %i in (0,1,2)do for %i in (0,1,2)do <con: call echo %ar[%i]%


  • В вашей/файл или командная строка, все следующие команды одинаковы, соблюдая замену%iс%%iв случае использования в/файлы
for %i in (0,1,2) do %ComSpec% /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec% /v:on /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe /v:on /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd /v:on /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd /v:on /c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec% /v /r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec% /v /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe /v /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe /v /c "echo !ar[%i]!"

for %i in (0,1,2) do cmd /v /r "echo !ar[%i]!"
for %i in (0,1,2) do cmd /v /c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd/v:on/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd/v:on/c "echo !ar[%i]!"

for %i in (0,1,2) do %ComSpec%/v/r "echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v/c "echo !ar[%i]!"

for %i in (0,1,2) do cmd/v/r "echo !ar[%i]!"
for %i in (0,1,2) do cmd/v/c "echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v:on/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v:on/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd/v:on/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd/v:on/c"echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v/r"echo !ar[%i]!"
for %i in (0,1,2) do %ComSpec%/v/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd.exe/v/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd.exe/v/c"echo !ar[%i]!"

for %i in (0,1,2) do cmd/v/r"echo !ar[%i]!"
for %i in (0,1,2) do cmd/v/c"echo !ar[%i]!"


for %i in (0,1,2) do %ComSpec%/v/recho !ar[%i]!
for %i in (0,1,2) do %ComSpec%/v/cecho !ar[%i]!

for %i in (0,1,2) do cmd.exe/v/r"echo !ar[%i]!
for %i in (0,1,2) do cmd.exe/v/c"echo !ar[%i]!

for %i in (0,1,2) do cmd/v/recho !ar[%i]!
for %i in (0,1,2) do cmd/v/cecho !ar[%i]!

решение2

Отложенное расширение в основном используется для расширения значений переменных более одного раза, особенно в циклах For.

Цитата изhttps://ss64.com/nt/delayedexpansion.html

Отложенное расширение приведет к тому, что переменные в пакетном файле будут расширяться во время выполнения, а не во время анализа. Эта опция включается с помощью команды SETLOCAL EnableDelayedExpansion.

Расширение переменной означает замену переменной (например, %windir%) ее значением C:\WINDOWS

По умолчанию расширение будет происходить только один раз, перед выполнением каждой строки. !Отложенное! расширение выполняется каждый раз при выполнении строки или для каждого цикла в команде цикла FOR. Сначала рассмотрим этот пример:

set i=0
for /l %%a in (0,1,10) do (
  set /a i=%i%+1
  echo %i%
)

Здесь мы ожидаем, что это выведет числа от 1 до 10. Однако каждый раз будет выведено только 0. Поскольку каждый раз переменные расширяются один раз, поэтому значения не меняются.

На этот раз мы попробуем отложенное расширение:

setlocal enabledelayedexpansion 
set i=0
for /l %%a in (0,1,10) do (
  set /a i=!i!+1
  echo !i!
)

На этот раз мы получим ожидаемый результат, поскольку каждый раз, когда мы расширяем значения, значение изменяется.

Ваш код не работает, потому что вам нужно добавить setlocal enabledelayedexpansionв начале кода.

надеюсь, это поможет

Связанный контент