One trick thing in Windows Batch is how it substitutes variable value: Variable expansion occurs when the command is read. This is especially confusing in if, for and multiple statements block.
If you run the following script, the output would be: VAR is still: first here: first
set VAR=first
if "%VAR%" == "first" (
set VAR=second
echo VAR is still: first here: %VAR%
if "%VAR%" == "second" @echo You will never see this
)
The batch processor treats the whole if block as one command, it expands the variables once and only once, before it executes the if block, so it is actually run:
if "first" == "first" (
set VAR=second
echo VAR is still: first here: first
if "first" == "second" @echo You will never see this
)
EnableDelayedExpansion
The rescue is Delayed Expansion, it will cause variables to be expanded at execution time rather than at parse time. It is enabled by Setlocal EnableDelayedExpansion, and use !variable_name! to tell batch processor to expand variable value at runtime.
Practical Example: Get jvm.dll path
When use Apache Procrun to wrap one Java Application as windows Services as described in this article: Windows BAT: Using Apache Procrun to Install Java Application As Windows Service
We try to get jvm.dll path from environment variable: APP_JAVA_HOME, JAVA_HOME, and set its value to PR_JVM.
The final version is like this:
@echo off
setlocal enabledelayedexpansion
set PR_JVM=auto
IF "%PR_JVM%" == "auto" (
set NEW_JAVA_HOME=%CV_JAVA_HOME%
if "!NEW_JAVA_HOME!" == "" (
set NEW_JAVA_HOME=%JAVA_HOME%
)
echo Using NEW_JAVA_HOME: !NEW_JAVA_HOME!
IF not "!NEW_JAVA_HOME!" == "" (
echo !NEW_JAVA_HOME! not empty, try to use it.
for /F "delims=" %%i in ('dir "!NEW_JAVA_HOME!" /B /S /a-d ^| findstr jvm.dll') do (
echo found "%%i"
set "NEW_PR_JVM=%%i"
)
IF exist "!NEW_PR_JVM!" (
SET "PR_JVM=!NEW_PR_JVM!"
echo NEW_PR_JVM: !NEW_PR_JVM!
)
)
)
echo final PR_JVM: %PR_JVM%
"%PRUNSRV%" "//US//%SERVICE_NAME%" --DisplayName "%PR_DESPLAYNAME%" --Description "%PR_DESCRIPTION%" --StdOutput auto --StdError auto ^
--Classpath="%MYCLASSPATH%" --Jvm="%PR_JVM%" --JvmOptions="%PR_JAVA_OPTIONS%" --StartPath "%APP_HOME%" --Startup=auto ^
--StartMode=jvm --StartClass=StartClass --StartParams="%START_PARAMS%" ^
--StopMode=jvm --StopClass=StopClass --StopParams="%STOP_PARAMS%"
ENDLOCAL
The output looks like:
Using NEW_JAVA_HOME: C:\Program Files\Java\jdk1.6.0_38
C:\Program Files\Java\jdk1.6.0_38 not empty, try to use it.
found "C:\Program Files\Java\jdk1.6.0_38\jre\bin\server\jvm.dll"
NEW_PR_JVM: C:\Program Files\Java\jdk1.6.0_38\jre\bin\server\jvm.dll
final PR_JVM: C:\Program Files\Java\jdk1.6.0_38\jre\bin\server\jvm.dll
The origin version is like, which doesn't work because how batch processor expand variable value:
@echo off
setlocal
set PR_JVM=auto
rem set CV_JAVA_HOME=
IF "%PR_JVM%" == "auto" (
set NEW_JAVA_HOME=%CV_JAVA_HOME%
if "%CV_JAVA_HOME%" == "" (
set NEW_JAVA_HOME=%JAVA_HOME%
)
echo Using NEW_JAVA_HOME: %NEW_JAVA_HOME%
IF not "%CV_JAVA_HOME%" == "" (
echo %CV_JAVA_HOME% not empty, try to use it.
for /F "delims=" %%i in ('dir "%CV_JAVA_HOME%" /B /S /a-d ^| findstr jvm.dll') do (
echo found "%%i"
set "NEW_PR_JVM=%%i"
)
IF exist "%NEW_PR_JVM%" (
SET "PR_JVM=%NEW_PR_JVM%"
echo NEW_PR_JVM: %NEW_PR_JVM%
)
)
)
echo final PR_JVM: %PR_JVM%
ENDLOCAL
The output looks like:
Using NEW_JAVA_HOME:
C:\Program Files\Java\jre7 not empty, try to use it.
found "C:\Program Files\Java\jre7\bin\server\jvm.dll"
final PR_JVM: auto
Resources
EnableDelayedExpansion
Environment variable expansion occurs when the command is read
Batch file :How to set a variable inside a loop for /F
http://stackoverflow.com/questions/691047/batch-file-variables-initialized-in-a-for-loop
If you run the following script, the output would be: VAR is still: first here: first
set VAR=first
if "%VAR%" == "first" (
set VAR=second
echo VAR is still: first here: %VAR%
if "%VAR%" == "second" @echo You will never see this
)
The batch processor treats the whole if block as one command, it expands the variables once and only once, before it executes the if block, so it is actually run:
if "first" == "first" (
set VAR=second
echo VAR is still: first here: first
if "first" == "second" @echo You will never see this
)
EnableDelayedExpansion
The rescue is Delayed Expansion, it will cause variables to be expanded at execution time rather than at parse time. It is enabled by Setlocal EnableDelayedExpansion, and use !variable_name! to tell batch processor to expand variable value at runtime.
Practical Example: Get jvm.dll path
When use Apache Procrun to wrap one Java Application as windows Services as described in this article: Windows BAT: Using Apache Procrun to Install Java Application As Windows Service
We try to get jvm.dll path from environment variable: APP_JAVA_HOME, JAVA_HOME, and set its value to PR_JVM.
The final version is like this:
@echo off
setlocal enabledelayedexpansion
set PR_JVM=auto
IF "%PR_JVM%" == "auto" (
set NEW_JAVA_HOME=%CV_JAVA_HOME%
if "!NEW_JAVA_HOME!" == "" (
set NEW_JAVA_HOME=%JAVA_HOME%
)
echo Using NEW_JAVA_HOME: !NEW_JAVA_HOME!
IF not "!NEW_JAVA_HOME!" == "" (
echo !NEW_JAVA_HOME! not empty, try to use it.
for /F "delims=" %%i in ('dir "!NEW_JAVA_HOME!" /B /S /a-d ^| findstr jvm.dll') do (
echo found "%%i"
set "NEW_PR_JVM=%%i"
)
IF exist "!NEW_PR_JVM!" (
SET "PR_JVM=!NEW_PR_JVM!"
echo NEW_PR_JVM: !NEW_PR_JVM!
)
)
)
echo final PR_JVM: %PR_JVM%
"%PRUNSRV%" "//US//%SERVICE_NAME%" --DisplayName "%PR_DESPLAYNAME%" --Description "%PR_DESCRIPTION%" --StdOutput auto --StdError auto ^
--Classpath="%MYCLASSPATH%" --Jvm="%PR_JVM%" --JvmOptions="%PR_JAVA_OPTIONS%" --StartPath "%APP_HOME%" --Startup=auto ^
--StartMode=jvm --StartClass=StartClass --StartParams="%START_PARAMS%" ^
--StopMode=jvm --StopClass=StopClass --StopParams="%STOP_PARAMS%"
ENDLOCAL
The output looks like:
Using NEW_JAVA_HOME: C:\Program Files\Java\jdk1.6.0_38
C:\Program Files\Java\jdk1.6.0_38 not empty, try to use it.
found "C:\Program Files\Java\jdk1.6.0_38\jre\bin\server\jvm.dll"
NEW_PR_JVM: C:\Program Files\Java\jdk1.6.0_38\jre\bin\server\jvm.dll
final PR_JVM: C:\Program Files\Java\jdk1.6.0_38\jre\bin\server\jvm.dll
The origin version is like, which doesn't work because how batch processor expand variable value:
@echo off
setlocal
set PR_JVM=auto
rem set CV_JAVA_HOME=
IF "%PR_JVM%" == "auto" (
set NEW_JAVA_HOME=%CV_JAVA_HOME%
if "%CV_JAVA_HOME%" == "" (
set NEW_JAVA_HOME=%JAVA_HOME%
)
echo Using NEW_JAVA_HOME: %NEW_JAVA_HOME%
IF not "%CV_JAVA_HOME%" == "" (
echo %CV_JAVA_HOME% not empty, try to use it.
for /F "delims=" %%i in ('dir "%CV_JAVA_HOME%" /B /S /a-d ^| findstr jvm.dll') do (
echo found "%%i"
set "NEW_PR_JVM=%%i"
)
IF exist "%NEW_PR_JVM%" (
SET "PR_JVM=%NEW_PR_JVM%"
echo NEW_PR_JVM: %NEW_PR_JVM%
)
)
)
echo final PR_JVM: %PR_JVM%
ENDLOCAL
The output looks like:
Using NEW_JAVA_HOME:
C:\Program Files\Java\jre7 not empty, try to use it.
found "C:\Program Files\Java\jre7\bin\server\jvm.dll"
final PR_JVM: auto
Resources
EnableDelayedExpansion
Environment variable expansion occurs when the command is read
Batch file :How to set a variable inside a loop for /F
http://stackoverflow.com/questions/691047/batch-file-variables-initialized-in-a-for-loop