PowerShell で bool 値を文字列から生成する
PowerShell で文字列型のキーと任意の文字列と変換可能な型の値を使った XML ベースの限定的な簡易 KVS があったのだけど、bool 値の挙動がおかしいという不具合報告があった。
そう、私は PowerShell の自動型変換が XmlConvert.ToBoolean() のように働くことを期待していたのだが、実際にはそんなうまい仕組みにはなっていなくて、文字列は長さが0より大きい場合に真であり、長さが0の場合だけ偽であるのだ。
PS> [bool] [string] $false True PS> [bool] [string] $null False
bool 値を格納している項目が2つしかなく、それらが省略可能かつ省略時は false となる項目であったため、多くの場合で省略値としての $null を $false に変換していたことで発見が遅れ、非常にまずいことになりかねないところであった。
文字列 False が真であることで、次のような暗黙の変換も非常に危険である。
PS> Get-Member -in $obj Field1 Property System.String Field1 { get; set; } Field2 Property System.Boolean Field2 { get; set; } PS> $obj.Field1 = "No" PS> $obj.Field2 = "False" PS> Format-List -in $obj Field1 : No Field2 : True
このような直接的な代入文は書かないだろうが、型にゆるい PowerShell では、厳密に型指定していない変数に対して bool 値として評価できないものが格納されている…なんて可能性は多々あるだろう。数値を bool にしているつもりが、"0" という文字列を評価していたら $true として扱われる。*1
面倒なことに、
PS> $test = 0, "0", "false", "False", "FALSE" PS> $test|%{$t=$_;trap{@{$t=$_};continue} @{$t=[bool]::Parse($_)}}|Format-Table Name Value ---- ----- 0 文字列が有効な Boolean 型として認識されませんでした。 0 文字列が有効な Boolean 型として認識されませんでした。 false False False False FALSE False PS> $test|%{$t=$_;trap{@{$t=$_};continue} @{$t=[System.Xml.XmlConvert]::ToBoolean($_)}}|Format-Table Name Value ---- ----- 0 False 0 False false False False 文字列が有効な Boolean 型として認識されませんでした。 False 文字列が有効な Boolean 型として認識されませんでした。
なもんで、用途に応じて
PS> # 小文字にしてすべて判定 PS> $test|%{ [System.Xml.XmlConvert]::ToBoolean($([string] $_).ToLower()) } PS> # true, false 以外は false PS> $test|%{ [bool]::TryParse($_, [ref] $_) -and $_ } PS> # true, false 以外は null PS> $test|%{ if ([bool]::TryParse($_, [ref] $_)){$_}else{$null} }
などなどの対応が考えられるのだが、もっと手軽な方法はないのか?*2