JSONに記述されたデータを環境変数にセットするバッチファイル
JSONデータを解析し、指定したパターンに一致するデータがあれば、そのプロパティー名(「hoge.piyo」など)を名前とした環境変数にデータを設定するバッチファイルです。
ソースコード
[SetEnvFromJson.bat]
@echo off & goto DoExec <# ---------------------------------------- Usage: SetEnvFromJson -Pattern "name-pattern" -Data "json-data" [-Prefix "env-prefix"] SetEnvFromJson -Pattern "name-pattern" [-Prefix "env-prefix"] < json-file-data any-json-output-command | SetEnvFromJson -Pattern "name-pattern" [-Prefix "env-prefix"] ---------------------------------------- ### Batch file :DoExec rem -- 拡張構文が利用可能かどうかの確認 rem -- (「%~dp0」構文が正しく展開されず「~dp0」となったり Set コマンドに失敗したりしたら使用不可) setlocal set TEMP_VAR= set "TEMP_VAR=%~dp0" 2>NUL if errorlevel 1 goto NoExtensions if "%TEMP_VAR%"=="~dp0" goto NoExtensions if "%TEMP_VAR%"=="" goto NoExtensions endlocal rem -- 「SetEnvFromJson.bat」があるディレクトリに「'」文字が含まれても良いように事前に pushd する pushd "%~dp0" rem -- 「PARAMETERS」変数を使うため一時的に Setlocal する setlocal rem -- バッチファイルの引数全てを「PARAMETERS」に展開 set PARAMETERS=%* rem -- 「,」がスペース文字に変換されないように「^,」に置換 set "PARAMETERS=%PARAMETERS:,=^,%" rem -- 「"」がPowerShellによって文字列の括りと解釈されるように「\"」に置換 rem -- (置換しないとプログラム引数としての「" "」と扱われ除去されてしまう) set "PARAMETERS=%PARAMETERS:"=\"%" rem -- Endlocal で「PARAMETERS」変数をリセットしつつ、その値を PowerShell の引数に指定し、 rem -- PowerShell の実行結果(出力)を For に渡す rem -- (「2^>CON」が必要な理由は後述) endlocal & for /f "tokens=1,* delims==" %%A in (' ^ PowerShell.exe -Command "& (iex -Command ('{#' + ((gc '%~nx0') -join \"`n\") + '}'))" %PARAMETERS% 2^>CON ^ ') do ( rem -- 出力は行ごとに「name=value」となっており A に name, B に value が rem -- 入るため、Set の左側に A の値、右側に B の値を指定する set "%%A=%%B" ) popd exit /b 0 rem -- 拡張構文が利用できない場合、0 以外の終了コードを返す rem -- Setlocal で改めて拡張構文を有効にしつつ終了させるが、それもできない場合は Goto で末尾に飛ばす rem -- (拡張構文が利用できない場合「goto :EOF」も不可) :NoExtensions echo You must enable extensions first. setlocal enableextensions exit /b 1 goto Finish ---------------------------------------- ### PowerShell script #> # 「Prefix」と「Data」は省略可能とし、「Data」を省略した場合は標準入力からデータを受け取る Param( [parameter(Mandatory=$true)][string] $Pattern, [string] $Prefix, [string] $Data ) $baseJson = "" if ($Data -eq $null) { $baseJson = [System.Console]::In.ReadToEnd() } else { $baseJson = $Data -join ' ' } if ($Prefix -eq $null) { $Prefix = "" } # 文字列をJSONデータとしてオブジェクトに変換 $obj = $baseJson | ConvertFrom-Json # 得たオブジェクトからパターンに一致するメンバを取り出す # ($Pattern が "A.B.*" なら $obj.A.B 以下すべてを得る) $nextTargetObj = $obj $targetMembers = $null $prefixName = "" $nextPrefixName = $Prefix ($Pattern -split "\.") | ForEach-Object { $obj = $nextTargetObj $prefixName = $nextPrefixName if ($obj -ne $null) { $a = $nextTargetObj | Get-Member $_ -MemberType NoteProperty if ($a.Length -eq 0) { $targetMembers = $null $nextTargetObj = $null } else { $targetMembers = $a $nextTargetObj = $obj.($targetMembers[0].Name) $nextPrefixName = $_ + "." } } } # メンバが得られたら、「名前=値」という形式でそれらを出力する # (呼び出し元バッチファイルの For 文でそれを解析する) if ($targetMembers -ne $null) { $targetMembers | ForEach-Object { Write-Output ($prefixName + $_.Name + "=" + $obj.($_.Name)) } } # 「:Finish」と記述してもエラーにならないように「:Finish」という関数を用意 function :Finish { } # これにより、以下の1行がバッチとしてもPowerShellとしても有効になる :Finish
使い方
例1
(コマンド実行例)
SetEnvFromJson.bat -Pattern "*" -Data '{ Data1: "Hoge", Data2: "Piyo"}' echo Data1=%Data1%, Data2=%Data2%
例2
(ファイル例: Settings.json)
{ MyProgram: { Disabled: true, DefaultName: "Rola" } }
(実行するバッチファイル)
@echo off setlocal enableextensions call SetEnvFromJson -Pattern "MyProgram.*" -Prefix MyEnv. < Settings.json if not "%MyEnv.MyProgram.Disabled%"=="" goto Finish MyProgram output.log :Finish
解説など
- 大まかな流れは「バッチファイルで引数を準備」→「PowerShellでJSONを解析」→「解析結果をバッチファイルに戻して環境変数に設定」です。
- このバッチファイルは「バッチファイルにPowerShellスクリプトを埋め込む その2」の方法を応用しています。
- PowerShell内で環境変数を設定してしまうと、それらの内容はPowerShell内でしか利用できないため、バッチファイル呼び出し元でも環境変数を伝播できるように結果をバッチファイルに戻す必要があります。
- PowerShellは、標準出力の出力先がリダイレクトされており(System.Console.IsOutputRedirected が True)、標準エラー出力の出力先がリダイレクトされていない(System.Console.IsErrorRedirected が False)場合、PowerShell内のエラーの出力先が標準出力となるようで、標準エラー出力を明示的にリダイレクトしておく必要があります。
- Forは出力をキャプチャーするために標準出力をリダイレクトしています。