プロジェクト

全般

プロフィール

Windows JDK環境切替バッチファイル

はじめに

Javaプログラミング環境では、複数のJava Development Kit(JDK)バージョンをインストールしておき、必要に応じて切り替えるということがよくあります。2018年3月時点であれば、例えば、JDK 8u152、JDK 8u162、JDK 9.0.1、JDK 9.0.4、JDK 10 といったJDKバージョンがインストールされていることでしょう。

Javaの開発環境では、JDKのコマンドラインツールが格納されているディレクトリを環境変数PATHに設定すること、JDK自体のディレクトリを環境変数JAVA_HOMEに設定することが主な設定になります。

  • 環境変数PATHは、作業ディレクトリ上でjavacコマンドなどをフルパス指定せずにコマンド名だけで実行するのに必要です。
  • 環境変数JAVA_HOMEは、Ant、Gradleなどの各種ツールがJDKの場所を参照するのに必要です。

Windowsマシンでは、環境変数の設定はWindows OSのコントロールパネルから行います。しかし、使うJDKのバージョンを切り替える度にコントロールパネルから環境変数の設定を修正するのは大変面倒ですし、タイプミスを誘発しやすいのも厄介です。

JDKの切り替えは行わない、常に最新版を1つだけ使用する、という人であっても、Javaは3か月に1回CPU(Critical Patch Update)が出るのでそれをインストールするとディレクトリが

  • C:\Program Files\Java\JDK1.8.0_152
  • C:\Program Files\Java\JDK1.8.0_162

のように、Update番号を含むものとなっています。このためアップデートをする度に環境変数PATHとJAVA_HOMEを修正する必要が生じます。

こうしたJDKバージョンの切り替え時に、簡単に使いたいJDKのバージョン用に環境変数PATHとJAVA_HOMEを設定するためのバッチファイルを作成します。
これは、コマンドプロンプト上でバッチファイルを実行し、そのコマンドプロンプト上で使用するJDK用の環境変数を設定するものです。複数のコマンドプロンプトを開き、それぞれで別のバージョンのJDKの設定をするといった使い分けも可能です。

バッチファイルの最初の1歩は、コマンドライン引数にバージョン番号を取り、if文で判別してPATHとJAVA_HOMEに値をセットするというものですが、JDKのバージョンが増えるたび(3か月に一回以上の頻度)if文とPATHとJAVA_HOMEに値をセットする記述を追加しなくてはなりません。

if "%1"=="1.8.0_152" (
    set JAVA_HOME=C:\Program Files\Java\JDK1.8.0_152
    PATH=%JAVA_HOME%\bin;%PATH%
) else if "%1"=="1.8.0_162" (
    set JAVA_HOME=C:\Program Files\Java\JDK1.8.0_162
    PATH=%JAVA_HOME%\bin;%PATH%
)

そこで、Windows OS版のJDKがインストール時にレジストリ上にインストールしたディレクトリの情報を書いているのを読み取り、バッチファイル上にはバージョン番号とディレクトリを一切記述しないで済むようなものを作れば、3か月に1回以上の頻度でリリースされるJDKに対してバッチファイルの修正をしなくても対応できるようになります。

制約事項もあります。

  • システム/ユーザー用の環境変数設定を書き換えるものではありません。
  • Windowsのインストーラーを使わずにインストールしたJDK(zipで固めたファイル群を展開しただけ、等)はこのバッチファイルの設定対象外となります。

バッチファイルのリポジトリ

GitHubのGistに登録しています。
https://gist.github.com/torutk/27f55630516286dd1478bbd8b032bc8a

Gistからファイルを取得すると、文字コードがUTF-8、改行コードがLFになってしまうのは避けられない?
[RAW]ボタンのリンク、[Download ZIP]のどちらもダメでした。取得後、文字コードと改行コードを直す必要があるかも。

GistのWebブラウザ上で編集したファイルは、文字コードUTF-8、改行コードLFとして扱われます。
そこで、Gistをリモートの git サーバーとみなして、ローカル環境のcore.autocrlfをfalseに設定し、文字コードをShit-JIS系、改行コードをCR+LFで記述したファイルをコミット&プッシュすれば、Gist上で[RAW]ボタンあるいは[Download ZIP]で取り出したファイルがWindowsの日本語環境で実行可能になります。

使用イメージ

Windowsのコマンドプロンプトから次のようにバッチファイルを実行します。引数を指定しない場合は、WindowsにインストールされているJDKのうち「Current Version」として登録されているバージョンのJDK用の環境変数を設定します。通常、最も新しいバージョンのJDKとなります。

D:\dev> setjdk
JDK version not specified, so using CurrentVersion=10
set JAVA_HOME=C:\Program Files\Java\jdk-10
append C:\Program Files\Java\jdk-10\bin to PATH
D:\dev>

バージョン番号を指定してバッチファイルを実行すると、そのバージョンのJDK用の環境変数を設定します。

D:\dev> setjdk 9
set JAVA_HOME=C:\Program Files\Java\jdk-9.0.4
append C:\Program Files\Java\jdk-9.0.4\bin to PATH
D:\dev>

32bit版のJDKを指定することもできます。バージョン番号の前に32を指定します1

D:\dev> setjdk 32 8
set JAVA_HOME=C:\Program Files (x86)\Java\jdk1.8.0_162
append C:\Program Files (x86)\Java\jdk1.8.0_162\bin to PATH
D:\dev>

1 JDK 32がリリースされるまでに指定方法を変更しないとまずいですね。

動作の原理

バッチファイル化する動作の仕組みを記述します。

JDKをインストールすると生成されるレジストリ

Windows版のJDKは、インストールするとレジストリに設定を書き込みます。

JDK 8までのレジストリ構造

JDK 8の場合は、次の表に示すレジストリキー、値の名前、値のデータが生成されます(設定に必要なもののみ抜粋)。

キー HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit
値の名前 CurrentVersion
値の種類 REG_SZ
値のデータ(例) 1.8

上述のキー(JDKのルートキー)には、マシンにインストールされているJDKの最新バージョンがいくつかを示すCurrentVersionの値があります。

次の表は、上述のJDKのルートキーに、そのCurrentVersionの値をサブキーとしたキーとその値を示したものです。

キー HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit/1.8
値の名前 JavaHome
値の種類 REG_SZ
値のデータ(例) C:\Program Files\Java\jdk1.8.0_162

このキーには、JDKをインストールしたディレクトリを示すJavaHomeがあります。JDK 8の最新アップデートを使用したい場合は、JDKのルートキーに"1.8"をサブキーに付けたものを検索すればよいということが分かります。

次の表は、JDKのルートキーに、アップデート番号までを含んだフルバージョン名を追加したキーとその値を示したものです。

キー HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit/1.8.0_162
値の名前 JavaHome
値の種類 REG_SZ
値のデータ(例) C:\Program Files\Java\jdk1.8.0_162

このキーにもJDKをインストールしたディレクトリを示すJavaHomeがあります。

JDK 8u152, JDK 8u162がインストールされたWindowsマシンのコマンドプロンプトから、最初のキー以下の内容を表示した結果を示します。

D:\>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit" /s

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit
    CurrentVersion  REG_SZ    1.8

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8
    JavaHome        REG_SZ    C:\Program Files\Java\jdk1.8.0_162
    MicroVersion    REG_SZ    0

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8.0_152
    JavaHome        REG_SZ    C:\Program Files\Java\jdk1.8.0_152
    MicroVersion    REG_SZ    0

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8.0_152\MSI
    INSTALLDIR      REG_SZ    C:\Program Files\Java\jdk1.8.0_152\
    NOSTARTMENU     REG_SZ    0

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8.0_162
    JavaHome        REG_SZ    C:\Program Files\Java\jdk1.8.0_162
    MicroVersion    REG_SZ    0

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8.0_162\MSI
    INSTALLDIR    REG_SZ    C:\Program Files\Java\jdk1.8.0_162\
    NOSTARTMENU    REG_SZ    0

JDK 8までは、次の手順でマシンにインストールされた最新バージョン(注:JDK 8までの範囲での最新)のJDKのインストールディレクトリを調べます。

  1. HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kitをキーに、CurrentVersionの値を調べます。
  2. 1.で使用したキーに1.で調べたCurrentVersionの値を結合したキーを作成します。
    この例では、HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8となります。
  3. 2.で作成したキーのJavaHomeの値を調べます。
    この例では、C:\Program Files\Java\jdk1.8.0_162が取得されます。

JDK 8を指定するがアップデート番号はマシンにインストールされている最新のものを使えばよいという場合

  1. HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\1.8をキーに、JavaHomeの値を調べます。
    この例では、C:\Program Files\Java\jdk1.8.0_162が取得されます。

JDKのバージョン番号をアップデート番号まで含めて直接指定してそのJDKのインストールディレクトリを調べることもできます。

  1. HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\バージョン番号 をキーにJavaHomeの値を調べます。
    バージョン番号は、1.8.0_162 のようにレジストリで使用している番号です(JDK 8u162 のような製品名として使われている名称ではなく、内部バージョン名で使用されている番号)。

JDK 9からのレジストリ構造

JDK 9において、レジストリの構造が変わっています。次のドキュメントにこの件について言及があります。

「Java Platform, Standard Edition Installation Guide」の Windows Registry Settings 項
「Java Platform, Standard Edition Oracle JDK 9 Migration Guide」の Windows Registry Key Changes 項

JDK 9からは、次のレジストリキー、値の名前、値のデータが生成されます(設定に必要なもののみ抜粋)。

キー HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK
値の名前 CurrentVersion
値の種類 REG_SZ
値のデータ(例) 10

上のキーに、マシンにインストールされているJDKの最新バージョンがいくつかを示すCurrentVersionがあります。

キー HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\10
値の名前 JavaHome
値の種類 REG_SZ
値のデータ(例) C:\Program Files\Java\jdk-10

バージョン番号付きのキーには、そのバージョンのJDKをインストールしたディレクトリを示すJavaHomeがあります。

キー HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.4
値の名前 JavaHome
値の種類 REG_SZ
値のデータ(例) C:\Program Files\Java\jdk-9.0.4

最新バージョン以外のJDKのバージョン(JDK 9以降)とそのインストールディレクトリもレジストリに存在します。

JDK 8までのレジストリより1段少なくなっています。JDK 8では、CurrentVersionが1.7とか1.8とかのメジャーバージョン番号までを含み、メジャーバージョンのレジストリキーに最新バージョンへのJavaHomeがありました。また、各バージョン毎にレジストリキーが存在し、それぞれのキーもJavaHomeを持っています。

JDK 9では、CurrentVersionが9.0.4とか10(これはGA版を指す)とかのマイナーバージョンまでを含み、マイナーバージョンのレジストリキーにJavaHomeがあります。

JDK 9.0.1、 JDK 9.0.4およびJDK 10がインストールされたWindowsマシンのコマンドプロンプトから、最初のキー以下の内容を表示した結果を示します。

D:\>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK" /s

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK
    CurrentVersion  REG_SZ    10

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\10
    JavaHome        REG_SZ    C:\Program Files\Java\jdk-10

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\10\MSI
    INSTALLDIR      REG_SZ    C:\Program Files\Java\jdk-10\
    NOSTARTMENU     REG_SZ    0

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.1
    JavaHome        REG_SZ    C:\Program Files\Java\jdk-9.0.1

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.1\MSI
    INSTALLDIR      REG_SZ    C:\Program Files\Java\jdk-9.0.1\
    NOSTARTMENU     REG_SZ    0

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.4
    JavaHome        REG_SZ    C:\Program Files\Java\jdk-9.0.4

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.4\MSI
    INSTALLDIR      REG_SZ    C:\Program Files\Java\jdk-9.0.4\
    NOSTARTMENU     REG_SZ    0

JDK 9からは、次の手順でマシンにインストールされた最新バージョン(注:JDK 9以降の最新)のJDKのインストールディレクトリを調べます。

  1. HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDKをキーに、CurrentVersionの値を調べます。
  2. 1.で使用したキーに1.で調べたCurrentVersionの値を結合したキーを作成します。
    この例では、HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\10となります。
  3. 2.で作成したキーのJavaHomeの値を調べます。
    この例では、C:\Program Files\Java\jdk-10が取得されます。
JDK 9を指定するがアップデート番号はマシンにインストールされている最新のものを使えばよいという場合
  • 問題発生
    HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit\9 というキーは、2017年9月にリリースされたJDK 9のGA版(General Availability版)のものです。このJDK 9 GA版をアンインストールすると、このキーもなくなります。
  • 回避策
    JDK 9.0.1、9.0.4 の2つのJDK 9バージョンがインストールされている場合、バッチファイルで指定するバージョンは9.0.1、9.0.4のようにフルに記述する必要があります。
  • 望む対処
    バッチファイルの引数で9を指定したときに、JDK 10以降が存在していても、JDK 9の最新版の9.0.4を設定することです。

JDKのバージョン番号をアップデート番号まで含めて直接指定してそのJDKのインストールディレクトリを調べることはできます。

  1. HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\バージョン番号 をキーにJavaHomeの値を調べます。
    バージョン番号は、9.0.4 のようにレジストリで使用している番号です。

Windows OS 64bit版での32bit環境用レジストリ

Windows OS 64bit版では、64bitアプリケーションの動作は勿論のこと、32bitアプリケーションを動作させるため32bitエミュレーション機能(WOW64)があります。
レジストリも、32bitアプリケーションがアクセスするキーと64bitアプリケーションがアクセスするキーがあり、両者が競合しないよう特殊なキーのツリーが用意されています。

HKEY_LOCAL_MACHINE
  + SOFTWARE
      +- Adobe
      +- AMD
      :
      +- JavaSoft
      :
      +--WOW6432Node
           +- Adobe
           +- AMD
           :
           +- JavaSoft
           :

32bitアプリケーションが、レジストリキーHKEY_LOCAL_MACHINE\SOFTWARE\JavaSoftを参照すると、実際にはHKEY_LOCAL_MACHINE\SOFTWARE\WO6432Node\JavaSoftが参照されます。これによって同じキーでも32bitアプリケーションが使用する場所と64bitアプリケーションが使用する場所がレジストリ上分離されます。

したがって、Windows OS 64bit版で、JDK 32bit版を使用するときは、レジストリのキーとしてHKEY_LOCAL_MACHINE\SOFTWARE\WO6432Node\JavaSoft\Java Development Kitを引く必要があります。

なお、regコマンドの場合、便利なオプションとして/reg:32が用意されており、本来のキーを指定し、このオプションを指定すると、内部で32bit用のキーが使われます。

バッチファイルの作成

先に記述した動作の原理に基づいて、バッチファイルを作成しました。

コマンドライン引数

コマンドライン引数の仕様

コマンドライン引数では次の指定を可能としています。

  • ヘルプ表示
  • JDKバージョンの指定
    • メジャーバージョンのみ指定(例:8, 9, 10)
    • フルバージョン指定(例:1.8.0_162, 9.0.4, 10)
      注)10はGeneral Availability版(メジャーバージョンの最初のリリース)のときフルバージョンになる
  • 32bit JDKの指定
  • JDKバージョン指定なし(マシンの「カレントバージョン」を使用)

コマンドライン引数の書式は次のように定義しました。

setjdk { /? | [32] [JDKバージョン] }
  • /? は、ヘルプ表示して終了する指定。引数の最初に指定する。
  • 32 は、JDKのうち32bit版を使用する指定。引数の最初に指定する。省略可。
  • JDKバージョン は、使用するJDKのバージョンを指定する文字列。

コマンドライン引数の処理

バッチファイルでは、コマンドライン引数を変数%1, %2, %3, ..., %9に最大9個まで格納して実行されます。

ヘルプ(/?)の処理
if "%1"=="/?" goto option_error
(中略)
:option_error
echo Usage: %0 { /? ^| [32] [^<jdk version^>] } 

最初の引数が%1に入ります。if文でヘルプオプション/?と一致するか判別し、バッチの後ろにあるエラー表示へジャンプします。

32bit版指定の処理
set JDK_32BIT=0
if "%1"=="32" (
   set JDK_32BIT=1
   @shift
)

32bitの指定有無を変数JDK_32BITに数値の0か1で設定します。初期値は32bit指定無の0としておきます。
最初の引数(%1)をif文で32bit版指定のオプション32と一致するか判別し、JDK_32BITの値を1にしています。
続くJDKバージョン指定の処理で、32指定の有無によりバージョン指定の引数が%2か%1か変わるのを避けるため、32指定があったときは@shiftで%2を%1にずらしています。

JDKバージョン指定の処理(8を1.8に変える)
if "%1"=="8" (
    set JDK_VERSION=1.8
) else (
    set JDK_VERSION=%1
)

JDKバージョン処理の指定で、JDK 8はJDKの内部では1.8となっているため、8が指定されたときは1.8に差し替えています。それ以外のバージョン指定の場合はそのままとして、いずれも変数JDK_VERSIONに格納しておきます。

変数の有効範囲の制御

バッチファイルを実行したときに、バッチファイルの中で定義・使用された変数は、バッチファイル終了後にも有効となります。しかし、バッチファイル内でしか使わない変数はバッチファイル終了後には未定義となるのが望ましいです。そこで、setlocal、endlocalを使ってバッチファイル内でのみ有効な変数とバッチファイル実行後にも有効とする変数を区別します。

setlocal
set JAVA_HOME=
set JDK_VERSION=
set JDK_KEYPATH_OLD="HKLM\SOFTWARE\JavaSoft\Java Development Kit" 
set JDK_KEYPATH_NEW="HKLM\SOFTWARE\JavaSoft\JDK" 
set JDK_KEYPATH=

setlocalを実行すると、それ以降endlocalが実行されるまでの区間で変数に代入された値は、setlocal~endlocalの間でのみ有効となります。
ここでは、JDK_VERSION, JDK_KEYPATH_OLD, JDK_KEYPATH_NEW, JDK_KEYPATHが、バッチファイル内(setlocal~endlocal)で有効な変数となります。
(この後に定義される変数も含めて)

変数JAVA_HOMEはバッチファイル終了後も有効とする環境変数です。endlocalを実行時に次のように記述するとバッチファイル終了後に有効となります。

endlocal & set JAVA_HOME=%JAVA_HOME%
PATH=%JAVA_HOME%\bin;%PATH%

コメントについて

REMはコメントではなく何もしないコマンド

バッチファイルでは、REMで始まる行がコメントであると認識していました。実際にはREMコマンドが実行され、REMコマンドは何もしないコマンドのため、あたかもコメント行のように認識されていました。

REM これはバッチファイルです。

ところが、コマンドなので、

REM /?
とすると、REMコマンドのヘルプを表示してしまいます。

D:\>rem /?
バッチ ファイルまたは CONFIG.SYS にコメント (注釈) を記録します。

REM [コメント]

D:\>

ということで、次の様にREMを使ってコメントを書いたら、バッチファイル実行時にエラーとなってしまいました。

REM Usage:
REM   setjdk {/? | [32] [<JDK Version>] }
REM   /?   show usage only
REM   32   use 32bit JDK
REM   <JDK Version>  use this version of JDK

回避策として、コメントをREM(コマンド)ではなく、::で記述しました。

:: Usage:
::   setjdk {/? | [32] [<JDK Version>] }
::   /?   show usage only
::   32   use 32bit JDK
::   <JDK Version>  use this version of JDK

バッチファイルでは、行頭に:があるとラベルとして扱われます。
::は、ラベルとしては無効な定義ですが、無効なラベルとして残るので、コメントとして利用することができます。

JDKのレジストリキーの新旧対応

JDKのレジストリキーは、JDK 8までとJDK 9からとで異なっています。

JDKバージョン JDKのレジストリルートキー
8まで HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\Java Development Kit
9から HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK

そこで、コマンドラインで指定されたJDKバージョンの値によって切り替えます。JDKバージョンの文字列が、1.7, 1.8のように1.で開始されていれば旧レジストリキーを使用します。1.以外であれば新レジストリキーを使用します。

if "%JDK_VERSION:~0,2%"=="1." (
    set JDK_KEYPATH=%JDK_KEYPATH_OLD%
    goto search_jdk
) else if not "%JDK_VERSION%"=="" (
    set JDK_KEYPATH=%JDK_KEYPATH_NEW%
    goto search_jdk

変数JDK_VERSIONには、"1.8"あるいは"1.8.0_162"が入ります。完全なバージョン文字列は四半期ごとにJDKのアップデート(セキュリティ脆弱性の対応を中心とし、3ヶ月ごとにリリース)が発生し、増えていくので、if文で、if "%JDK_VERSION"=="1.8.0_162"などと判定していてはバッチファイルが大変なことになってしまいます。

そこで、変数の文字列の部分文字列を特殊な書き方(%JDK_VERSION:~0,2%)で取り出します。この例は、JDK_VERSION変数に代入された文字列の0番目(先頭)から2文字を部分文字列として取り出しています。

レジストリキーの検索

CurrentVersionの値の取得

JDKバージョンが指定されなかったときは、JDKのルートキーに定義されるCurrentVersionの値を取得します。

if "%JDK_VERSION%"=="" (
    for /f "skip=2 tokens=2*" %%A in (
        'reg query %1 /v CurrentVersion %REG32%'
    ) do set JDK_VERSION=%%B
)

レジストリキーの値を読み取るには、regコマンドのqueryオプションを使用します。書式は
reg query キー /v 値
となります。%1には、JDKのルートキーが入っています。%REG32%は、32bit指定があった場合に/reg:32が入ります。

reg queryを単独で実行すると次のようになります。

D:\>reg query HKLM\SOFTWARE\JavaSoft\JDK /v CurrentVersion

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK
    CurrentVersion    REG_SZ    10

reg query実行結果で欲しいのは、CurrentVersion REG_SZ 10の行で10の値の部分です。
コマンド実行結果を行単位で処理し、ある部分を取り出したいときに、for /f コマンドが使えます。

for /fの基本書式は次です。
for /f オプション 変数 in (テキストファイル) do コマンド
テキストファイルに指定したファイルの中身を行単位に読み込み、行ごとにコマンドを実行します。コマンドでは、オプションにより切り出される文字列が変数に格納されるのでそれを利用します。
テキストファイルの指定の部分で、シングルクォートで囲った文字列を指定すると、テキストファイルではなく文字列をコマンドとして実行し、その実行結果が処理対象となります。

今回の例では、オプションでまずskip=2を指定しています。これは結果の最初の2行を無視します。for文で処理対象となるテキストは、reg queryの実行結果なので次となりますので(分かりやすいように行番号を追加しています)、最初の2行を飛ばして3、4行目だけが処理対象となります。

1 
2 HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK
3     CurrentVersion    REG_SZ    10
4

次にオプションでtokens=2* を指定しています。これは、デフォルトの区切り子(デリミター)で行を区切って、その2番目以降をforの変数%A, %B, %C, ... にセットします。
3行目は、デフォルトの区切り子で区切ると、1番目(tokens=1)がCurrentVersion、2番目(tokens=2)がREG_SZ、3番目(tokens=3)が10となります。tokens=2*の指定では、2番目をforの変数%Aに、3番目を変数%Bに、4番目を変数%Cに、・・・と入ります。
したがって、変数%Bの中に所望のバージョン番号が入っていることになります。これをバッチファイル内変数JDK_VERSIONに代入します。

  • 注)バッチファイルにfor文を記述するときは、forの変数は%Aなら%%Aと一つ%を追加して記述します。

指定したバージョンのJDKのサブキーを検索

setjdk.batのコマンドライン引数でJDKバージョンが指定された場合、あるいはCurrentVersionの値からJDKバージョンを取得した場合、次にJDKのルートキーのサブキーで実在するJDKバージョンのサブキーを検索します。

for /f "delims=" %%A in (
    'reg query %1 /f %JDK_VERSION% /k %REG32% 2^>nul ^| findstr HKEY_LOCAL'
) do set JDK_VERSION_KEY=%%A

reg queryを単独実行すると次のようになります。

D:\>reg query HKLM\SOFTWARE\JavaSoft\JDK /f 9 /k

HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.1
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.4
検索の完了: 該当 2 件

最後の行の「検索の完了」が余分です。forのオプション skipでは開始からの行であれば読み飛ばせますが、処理で必要とする行の後ろを読み飛ばすのは無理そうです。そこで、パイプで HKEY_LOCALを含む行だけ抽出します。実行結果は次のようになります。

D:\>reg query HKLM\SOFTWARE\JavaSoft\JDK /f 9 /k | findstr HKEY_LOCAL
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.1
HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft\JDK\9.0.4

for文では2回処理が動きますが、後の方が新しいバージョンになるので、毎回変数JDK_VERSION_KEYにセットする処理が実行され、最後の行の値が最終的にJDK_VERSIONにセットされます。reg queryの検索結果の順番が気になりますが、手元の環境ではレジストリ登録順に関わらず、文字列の昇順に並びました。

なお、for文の中でのコマンド実行文字列にパイプ記号やリダイレクト記号を使うには、^でエスケープします。

forのオプションで"delims="と区切り子をなしとしているのは、reg queryの結果に空白が含まれた場合、変数で取り出せる文字列が空白で区切られないようにするためです。

指定したキーのJavaHomeの値を取得

CurrentVersionの値の取得と同じ方法で取得できます。

for /f "skip=2 tokens=2*" %%A in (
    'reg query "%JDK_VERSION_KEY%" /v JavaHome %REG32%'
) do set JAVA_HOME=%%B

こうして、やっとJAVA_HOMEが確定します。

クリップボードから画像を追加 (サイズの上限: 1 GB)