¿Cómo funciona la expansión retrasada en un script por lotes?

¿Cómo funciona la expansión retrasada en un script por lotes?

Tome esta sencilla serie de comandos ejecutados en un entorno por lotes:

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

Produce lo siguiente:

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

Por lo tanto, obviamente no se expande a sus valores. ¿Cómo lo haría?

Respuesta1

Fuentes:
Fase 5en¿Cómo analiza los scripts el intérprete de comandos de Windows (CMD.EXE)?

Expansión retrasada:Sólo si la expansión retardada está activada, el comando no está en unabloque entre paréntesis a cada lado de una tubería, y el comando no es unscript por lotes "desnudo"(nombre del script sin paréntesis, CALL, concatenación de comandos o canalización).

  • Cada token de un comando se analiza de forma independiente para determinar la expansión retrasada.
    • La mayoría de los comandos analizan dos o más tokens: el token de comando, el token de argumentos y cada token de destino de redirección.
    • El comando FOR analiza únicamente el token de la cláusula IN.
    • El comando IF analiza solo los valores de comparación, uno o dos, según el operador de comparación.
  • Para cada token analizado, primero verifique si contiene algún archivo !. De lo contrario, el token no se analiza, lo cual es importante para ^los personajes. Si el token contiene !, escanee cada carácter de izquierda a derecha:
    • Si es un símbolo de intercalación ( ^), el siguiente carácter no tiene ningún significado especial, el símbolo de intercalación en sí se elimina.
    • Si es un signo de exclamación, busque el siguiente signo de exclamación (ya no se observan signos de intercalación), expanda al valor de la variable.
      • Las aperturas consecutivas !se colapsan en una sola!
      • Cualquier resto no emparejado !se elimina
    • Expandir vars en esta etapa es "seguro", porque ya no se detectan caracteres especiales (incluso <CR>o <LF>)
    • Para una explicación más completa, lea la segunda mitad de esto de dbenham. mismo hilo - Fase de signo de exclamación

Fase 5.3) Procesamiento de tuberías:Solo si los comandos están en cualquier lado de una tubería.
Cada lado de la tubería se procesa de forma independiente y asincrónica.

  • Si el comando es interno de cmd.exe, o es un archivo por lotes, o si es un bloque de comando entre paréntesis, entonces se ejecuta en un nuevo hilo de cmd.exe a través de %comspec% /S /D /c" commandBlock", por lo que el bloque de comando se reinicia por fase, pero esta vez en modo de línea de comando.
    • Si es un bloque de comando entre paréntesis, todos los <LF>que tengan un comando antes y después se convierten a <space>&. Otros <LF>están despojados.
  • Este es el final del procesamiento de los comandos de canalización.
  • Verhttps://stackoverflow.com/q/8192318/1012053para más información sobre el análisis y procesamiento de tuberías

Fase 5.5) Ejecutar Redirección:Cualquier redirección que se haya descubierto en la fase 2 ahora se ejecuta.



ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

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]!

  • En tus/presentar sin declararsetlocal enabledelayedexpansion, también puedes usarcmd.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
  • También puedes usarcallen/o en su línea de comando para actualizar este valor:
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]%


  • En tus/archivo o línea de comando, todos los siguientes comandos son iguales, observando el reemplazo%icon%%ien el caso de uso en/archivos
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]!

Respuesta2

La expansión retardada se utiliza principalmente para expandir valores de variables más de una vez, especialmente en bucles For.

Citando dehttps://ss64.com/nt/delayedexpansion.html

La expansión retrasada hará que las variables dentro de un archivo por lotes se expandan en el momento de la ejecución en lugar de en el momento del análisis; esta opción se activa con el comando SETLOCAL EnableDelayedExpansion.

La expansión de variables significa reemplazar una variable (por ejemplo, %windir%) con su valor C:\WINDOWS

De forma predeterminada, la expansión se producirá solo una vez, antes de que se ejecute cada línea. El !retrasado! la expansión se realiza cada vez que se ejecuta la línea, o para cada bucle en un comando de bucle FOR. Primero miramos este ejemplo:

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

Aquí esperamos que esto genere los números del 1 al 10. Sin embargo, cada vez generará solo 0. Debido a que cada vez que las variables se expanden una vez, los valores no cambian.

Esta vez intentaremos con la expansión retrasada:

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

Esta vez obtendremos el resultado esperado, porque cada vez que expandimos los valores, el valor cambia.

Su código no funciona porque debe agregarlo setlocal enabledelayedexpansional inicio de su código.

Espero que ayude

información relacionada