script logical functions files comment commands batch bat array scope batch-file

scope - logical - Problema raro del alcance en el archivo.bat



functions batch script (3)

Estoy escribiendo un archivo .bat simple y me he encontrado con un comportamiento extraño. Hay un par de lugares donde tengo que hacer un if / else simple, pero el código dentro de los bloques no parece estar funcionando correctamente.

Aquí hay un caso simple que demuestra el error:

@echo off set MODE=FOOBAR if "%~1"=="" ( set MODE=all echo mode: %MODE% ) else ( set MODE=%~1 echo mode: %MODE% ) echo mode: %MODE%

La salida que obtengo es:

C:/>test.bat test mode: FOOBAR mode: test

¿Por qué el eco dentro del bloque de código no obtiene el nuevo valor de la variable? En el código real que estoy escribiendo necesito construir algunas variables y hacer referencia a ellas dentro del alcance de if / else. Podría cambiar esto para usar etiquetas y gotos en lugar de if / else, pero eso no parece tan limpio.

¿Qué causa este comportamiento? ¿Hay algún tipo de límite en las variables dentro de los bloques de código?


Parece que las reglas de lectura y escritura utilizan diferentes ámbitos.

Si eliminas esta línea

set MODE=FOOBAR

funcionará como se espera. Por lo tanto, es probable que necesite tener una serie compleja si es necesario para obtener las variables que desee.


Te estás enfrentando al problema de la expansión de la variable estática de cmd. La variable MODE solo se evalúa una vez. Puedes ver esto si omites el @echo fuera de línea.

Desde el conjunto /? documentación:

Finalmente, se ha agregado soporte para la expansión variable de entorno demorado. Este soporte siempre está deshabilitado por defecto, pero puede habilitarse / deshabilitarse mediante el modificador de línea de comando / V a CMD.EXE. Ver CMD /?

La expansión de variables de entorno retrasadas es útil para evitar las limitaciones de la expansión actual que ocurre cuando se lee una línea de texto, no cuando se ejecuta. El siguiente ejemplo demuestra el problema con la expansión de variables inmediata:

set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )

nunca mostraría el mensaje, ya que el% VAR% en ambas declaraciones IF se sustituye cuando se lee la primera instrucción IF, ya que lógicamente incluye el cuerpo del IF, que es una declaración compuesta. Entonces, la IF dentro de la declaración compuesta realmente compara "antes" con "después", lo que nunca será igual. Del mismo modo, el siguiente ejemplo no funcionará como se esperaba:

set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%

ya que NO creará una lista de archivos en el directorio actual, sino que simplemente establecerá la variable LIST en el último archivo encontrado. Nuevamente, esto se debe a que% LIST% se expande solo una vez cuando se lee la instrucción FOR, y en ese momento la variable LIST está vacía. Entonces, el bucle FOR real que estamos ejecutando es:

for %i in (*) do set LIST= %i

que simplemente sigue configurando LIST al último archivo encontrado.

La expansión variable de entorno retrasada le permite usar un carácter diferente (el signo de exclamación) para expandir las variables de entorno en el momento de la ejecución. Si la expansión de la variable retrasada está habilitada, los ejemplos anteriores se pueden escribir de la siguiente manera para que funcionen como se esperaba:

set VAR=before if "%VAR%" == "before" ( set VAR=after if "!VAR!" == "after" @echo If you see this, it worked ) set LIST= for %i in (*) do set LIST=!LIST! %i echo %LIST%


setlocal EnableDelayedExpansion

habilitará la bandera / v