1

I want to extract the Java version on my machine.
1/ I have an Oracle Java 8 installed and the command where java outputs this:

C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe

2/ The command java -version outputs this:

java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)

3/ The command java -version 2>&1 | find "version" outputs this:

java version "1.8.0_221"

So I want to do the exact same thing as the last command within a batch file. So within a batch file, I have this code similar to step 2:

@echo off & setLocal enableExtensions enableDelayedExpansion
set /a count=0
for /f "tokens=*" %%j in ('where java') do (
    set /a count+=1
    set JAVA[!count!]=%%j
    call :echo_java
)
goto :eof

:echo_java
    set java_cmd="!JAVA[%count%]!" -version
    for /f "tokens=*" %%v in ('%java_cmd% 2^>^&1') do echo %%v
    exit /b 0

But as soon as I modify the function :echo_java to be the equivalent to step 3 like this:

:echo_java
    set java_cmd="!JAVA[%count%]!" -version
    for /f "tokens=*" %%v in ('%java_cmd% 2^>^&1 ^| find "version"') do echo %%v
    exit /b 0

I get this error:

'C:\Program' is not recognized as an internal or external command, operable program or batch file.

I suppose that there are some bad escaping happening here but I really can't see what's wrong.

aschipfl
  • 33,626
  • 12
  • 54
  • 99
OOEngineer
  • 447
  • 3
  • 12

1 Answers1

1

After variable expansion the line:

for /f "tokens=*" %%v in ('%java_cmd% 2^>^&1 ^| find "version"') do echo %%v

expands to something like:

for /f "tokens=*" %%v in ('"C:\Program Files (x86)\...\java.exe" 2^>^&1 ^| find "version"') do echo %%v

so the command line to be executed by for /F begins and ends with ".

for /F uses cmd /c to execute that command line, which removes the surrounding ", thus leaving this behind:

C:\Program Files (x86)\...\java.exe" 2>&1 | find "version

which is obviously incorrect syntax.

To avoid that, change the command line so that it is not surrounded by quotation marks (by moving the redirection portion here):

for /f "tokens=*" %%v in ('2^>^&1 %java_cmd% ^| find "version"') do echo %%v

Alternatively, provide explicit quotation marks that can be removed by cmd /C, so the remainder stays valid (escape these quotes in order not to have to alter other escape sequences):

for /f "tokens=*" %%v in ('^"%java_cmd% 2^>^&1 ^| find "version"^"') do echo %%v

Furthermore, I would like to suggest to modify the whole script a bit (see all the rem remarks):

@echo off
rem /* Begin the script with delayed expansion disabled as it might lead to problems
rem    during expansion of `for` meta-variables in case the values contain `!`: */
setlocal EnableExtensions DisableDelayedExpansion
rem // Use quoted `set` syntax (this requires the command extensions to be enabled):
set /A "count=0"
for /F "tokens=*" %%j in ('where java') do (
    set /A "count+=1"
    rem // Enable delayed expansion only when needed, like for variable `count` here:
    setlocal EnableDelayedExpansion
    rem /* (Mis-)use a `for` loop to make the value of `!count!` available even when
    rem    delayed expansion is disabled, hence after `endlocal` then: */
    for %%c in (!count!) do endlocal & set "JAVA[%%c]=%%j"
    rem /* Pass an argument over to the sub-routine rather than using variables
    rem    globally; in this situation this even does not need delayed expansion: */
    call :ECHO_JAVA "%%j"
)
endlocal
goto :EOF

:ECHO_JAVA
    rem /* Explicitly disable delayed expansion in the sub-routine, so you may call
    rem    it even from a code section with delayed expansion enabled: */
    setlocal DisableDelayedExpansion
    rem /* Use the sub-routine argument (`%~1`) herein, with explicit control of
    rem    quotation (`~` removes potential quotes, then we explicitly add such): */
    for /F "tokens=*" %%v in ('2^>^&1 "%~1" -version ^| find "version"') do echo(%%v
    endlocal
    exit /B 0
aschipfl
  • 33,626
  • 12
  • 54
  • 99
  • I can't believe that this kind of quirks do happen in Windows batch scripting... I also didn't know you could reverse the positions of the command and the redirection portions to the same results. Thanks a bunch! – OOEngineer Mar 19 '20 at 06:20