【PowerShell小ネタ】 ConvertFrom-StringDataで連想配列を作ったらつんのめった話

【PowerShell小ネタ】 ConvertFrom-StringDataで連想配列を作ったらつんのめった話

なんだこれはたまげたなぁ
えぇ…
※内容とは関係ないんだけど、「つんのめる」って方言だそう。普通に使ってたぞ。

ConvertFrom-StringDataのちょっとした注意点

PowerShellで自分用のツールを作っていたら変なところで問題が発生した話。

[KEY]=[VALUE]形式のテキストを読み込んで[ConvertFrom-StringData]コマンドレットで連想配列化、
あとはそれを使ってなんやかんや...というツールをさっと作った。
とりあえず動かしたら一応それっぽく動く。

※2018/5/24追記
Get-Contentでファイルを読み取り連想配列化するとき、
そのままConvertFrom-StringDataに渡すと上手くいかないという初見殺しがある模様。
詳細は下の記事。
【PowerShell】 ConvertFrom-StringDataが返すのはHashtableとは限らない!|コーヒースモーク

続いて、テキストの中身を変えてテスト。

…と、なんか挙動がおかしい。if文が思ったほうに流れない。
どうやら、数値の比較が上手くいってないっぽい。
いろいろ条件を変えてみるも、やっぱり挙動が変。

10 -gt 5 (10は5より大きい)という比較が偽になる。
なんだこりゃ

悩むこと3分ほど。
ふと原因に思い当たった。

原因は次の通り。

PS C:\work> $hoge_1 = @{fuga=10;piyo=20}
PS C:\work>
PS C:\work> $hoge_1.fuga -gt 5
True
PS C:\work>

# 普通に連想配列を作る場合は特に問題ない

PS C:\work> $hoge_2 = Write-Output fuga=10 | ConvertFrom-StringData
PS C:\work>
PS C:\work> $hoge_2.fuga -gt 5
False
PS C:\work>

# ところが、[ConvertFrom-StringData]で作ったものは比較結果が変

PS C:\work> $hoge_1.fuga.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Int32                                    System.ValueType

# 普通に作った連想配列のVALUEはちゃんとint型

PS C:\work> $hoge_2.fuga.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

#[ConvertFrom-StringData]で作るとStringに!!

つまりどういうことかというと…
 ・[ConvertFrom-StringData]で作った連想配列は数字であってもString型になる
 ・PowerShellは数値用の比較演算子が文字列でも使えるので自動では型変換されない
ということみたい。

これだけのことなので回避法はシンプル。
PS C:\work> [int]$hoge_2.fuga -gt 5
True

# とりあえずキャスト

PS C:\work> 5 -lt $hoge_2.fuga
True

# 比較の左右を入れ替えるとちゃんと型変換される
まじめに使うときは、[int]::TryParse とかでちゃんと入力値確認もしたほうがいいのかも。

0 件のコメント :

コメントを投稿