Windows 批处理脚本:尝试通过使用管道循环命令结果来提取 Java 版本
Windows batch script: trying to extract Java version by looping on command results with pipe
我想在我的机器上提取 Java 版本。
1/ 我安装了 Oracle Java 8,命令 where java
输出:
C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe
2/命令java -version
输出如下:
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/命令java -version 2>&1 | find "version"
输出如下:
java version "1.8.0_221"
所以我想在批处理文件中执行与最后一个命令完全相同的操作。所以在批处理文件中,我有类似于步骤 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
但是一旦我将函数 :echo_java
修改为等同于这样的第 3 步:
: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
我收到这个错误:
'C:\Program' is not recognized as an internal or external command, operable program or batch file.
我想这里发生了一些糟糕的转义,但我真的看不出有什么问题。
行变量扩展后:
for /f "tokens=*" %%v in ('%java_cmd% 2^>^&1 ^| find "version"') do echo %%v
扩展为类似:
for /f "tokens=*" %%v in ('"C:\Program Files (x86)\...\java.exe" 2^>^&1 ^| find "version"') do echo %%v
所以for /F
要执行的命令行以"
开始和结束。
for /F
使用 cmd /c
执行该命令行,删除周围的 "
,从而留下:
C:\Program Files (x86)\...\java.exe" 2>&1 | find "version
这显然是不正确的语法。
为避免这种情况,请更改命令行,使其不被引号括起来(通过将重定向部分移至此处):
for /f "tokens=*" %%v in ('2^>^&1 %java_cmd% ^| find "version"') do echo %%v
或者,提供可以被 cmd /C
删除的显式引号,以便其余部分保持有效(转义这些引号,以便不必更改其他转义序列):
for /f "tokens=*" %%v in ('^"%java_cmd% 2^>^&1 ^| find "version"^"') do echo %%v
此外,我建议稍微修改一下整个脚本(参见所有 rem
备注):
@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
我想在我的机器上提取 Java 版本。
1/ 我安装了 Oracle Java 8,命令 where java
输出:
C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe
2/命令java -version
输出如下:
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/命令java -version 2>&1 | find "version"
输出如下:
java version "1.8.0_221"
所以我想在批处理文件中执行与最后一个命令完全相同的操作。所以在批处理文件中,我有类似于步骤 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
但是一旦我将函数 :echo_java
修改为等同于这样的第 3 步:
: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
我收到这个错误:
'C:\Program' is not recognized as an internal or external command, operable program or batch file.
我想这里发生了一些糟糕的转义,但我真的看不出有什么问题。
行变量扩展后:
for /f "tokens=*" %%v in ('%java_cmd% 2^>^&1 ^| find "version"') do echo %%v
扩展为类似:
for /f "tokens=*" %%v in ('"C:\Program Files (x86)\...\java.exe" 2^>^&1 ^| find "version"') do echo %%v
所以for /F
要执行的命令行以"
开始和结束。
for /F
使用 cmd /c
执行该命令行,删除周围的 "
,从而留下:
C:\Program Files (x86)\...\java.exe" 2>&1 | find "version
这显然是不正确的语法。
为避免这种情况,请更改命令行,使其不被引号括起来(通过将重定向部分移至此处):
for /f "tokens=*" %%v in ('2^>^&1 %java_cmd% ^| find "version"') do echo %%v
或者,提供可以被 cmd /C
删除的显式引号,以便其余部分保持有效(转义这些引号,以便不必更改其他转义序列):
for /f "tokens=*" %%v in ('^"%java_cmd% 2^>^&1 ^| find "version"^"') do echo %%v
此外,我建议稍微修改一下整个脚本(参见所有 rem
备注):
@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