[PowerShell]System.Booleanのデータ型に変換する際の注意事項まとめ
概要
PowerShellでデータ型の変換(以降、キャスト)を行う際は 変換したいデータ型
+ 変換対象の値(もしくは対象変数名)
と定義することで実現可能です。
真偽であるSystem.Boolean
(bool
)にキャストする際も同じ手法で対応可能なのですが、
PowerShellではルールが複雑なため、実例を用いて注意事項を共有します。
PowerShellにおけるキャストの説明
bool
へのキャストから説明するとわかり難いと思うので、
まずは、とっつきやすい文字列型から数値型へのキャストを題材に説明します。
下記は文字列型から数値にキャストする際のコード。
$a = "10" # 文字列型で宣言
$num_a = [int]$a # 数値型に変換(キャスト)
$num_a.GetType().FullName # データ型を確認
# 結果「System.Int32」となり数値型に変換できた!
キャストするパターンとしないパターンの実例
下記のようなコードの場合、$a
と $b
それぞれが文字列型となっているため、そのまま数値計算できません。
# "10" という文字と "20" という文字。
[System.String]$a = "10"
[System.String]$b = "20"
# 数値を加算(期待する結果にならない)
$total = $a + $b
# -> 数値加算したいが文字列型のため、文字列結合
# データ型の種類を確認
$total.GetType().FullName
# -> 文字列型「System.String」となる
# 結果は数値加算ではなく文字列結合した "1020" になってしまう。
Write-Host $total
# -> ❌ 出力結果: 1020
下記のように文字列型の変数を数値型にキャストすることで数値計算が可能となります!
# "10" という文字と "20" という文字。
[System.String]$a = "10"
[System.String]$b = "20"
# 数値型にキャスト
$num_a = [System.Int32]$a
$num_b = [System.Int32]$b
# -> ✅ 両方とも数値型(System.Int32)となる
# 変換した数値で加算(期待する結果となる)
$total = $num_a + $num_b
# -> 数値加算される
# データ型の種類を確認
$total.GetType().FullName
# -> 数値型「System.Int32」となる
# 結果は数値加算されて 30 となる
Write-Host ($total)
# ✅ 出力結果: 30
これでPowerShellにおけるキャストの概要はおさえることができました。
つぎは本記事のテーマである bool
のキャストにおけるルールを明記します。
boolのキャストにおけるルール
基本的なルールは下記のとおり。
-
boolキャスト時に
$false
として判定されるもの- 数値の
0
$null
- 空文字列 (
""
) - 空の配列 (
@()
) - 【例外】要素が「数値の
0
」一個だけの配列 (@(0)
)
- 数値の
-
boolキャスト時に
$true
として判定されるもの- 上記
$false
の条件以外すべて
下記が条件の例-
0
以外のすべての数値 - 空ではないすべての文字列 (例:
"true"
,"false"
,"ok"
,"0"
) - 要素が1つ以上あるすべての配列 (ただし、上記の例外
@(0)
を除く)- 例:
[bool]@("false")
-> True (要素数1) - 例:
[bool]@("0")
-> True (要素数1) - 例:
[bool]@(0,0)
-> True (要素数2)
- 例:
-
- 上記
一覧にしてみると、すごくややこしいですよね。基本的に真偽を設定する場合は、
-
0
→$false
-
1
(0
以外) →$true
とするルールをベースとした方がシンプルだと思います。
boolキャストの使い時
わたしがよく利用するシーンとしては、設定ファイルから真偽の数値を読み取り、
コード上で真偽を判定する場所でboolにキャストすることが多いです。
設定ファイルの数値をそのまま使うのではなく、boolキャストするメリットとデメリットは下記のとおり。
boolキャストのメリット
- コードが簡潔、シンプルになりやすい
- 設定ファイルの値をそのまま流用して真偽の判定が可能
boolキャストのデメリット
- (場合によるが)可読性が悪くなる
- コードが統一しにくい
PowerShellにおけるboolキャストのルールが複数あるため。 - 不具合(バグ)の温床になりやすい
コードが統一しにくいため、人によってコードが変わる可能性。
実例
$falseと評価される値(Falsy Values)
if ($null) { $true } else { $false }
# > 結果:False
# > 「$null」は$falseと同義。
# 整数
if (0) { $true } else { $false }
# > 結果:False
# > 「0」は$falseと同義。
# 浮動小数点数
if (0.0) { $true } else { $false }
# > 結果:False
# > 「0.0」は$falseと同義。
if ("") { $true } else { $false }
# > 結果:False
# > 「""」(空文字列)は$falseと同義。
if (@()) { $true } else { $false }
# > 結果:False
# > 「@()」(空の配列)は$falseと同義。
if (@(0)) { $true } else { $false }
# > 結果:False
# > 「@(0)」は、bool評価時において特別に$falseと同義として扱われる。
$trueと評価される値(Truthy Values)
# 正の数
if (1) { $true } else { $false }
# > 結果:True
# > 「1」は$trueと同義。
# 負の数
if (-1) { $true } else { $false }
# > 結果:True
# > 「-1」は$trueと同義。
# 一般的な文字列
if ("hello") { $true } else { $false }
# > 結果:True
# > 空でない文字列は$trueと同義。
# "false"という文字列自体も、空ではないので$true
if ("false") { $true } else { $false }
# > 結果:True
# > 「"false"」という文字列は$trueと同義。
# "0"という文字列も、空ではないので$true
if ("0") { $true } else { $false }
# > 結果:True
# > 「"0"」という文字列は$trueと同義。
# 数値が1つ
if (@(1)) { $true } else { $false }
# > 結果:True
# > 要素が1つ以上ある配列は$trueと同義。
# 文字列"false"が1つ
if (@("false")) { $true } else { $false }
# > 結果:True
# > 要素が1つ以上ある配列は$trueと同義。
# 要素が複数
if (@(0, 0)) { $true } else { $false }
# > 結果:True
# > 要素が1つ以上ある配列は$trueと同義。
# 「$null」でないオブジェクト
$obj = [pscustomobject]@{}
if ($obj) { $true } else { $false }
# > 結果:True
# > 値はないがPSCustomObjectという器はあるので、「$null」でなく$trueと同義。
まとめ
PowerShellのbool
キャストについて、以下の点が重要です。
-
$false
と評価されるのは、$null
、0
、空文字列、空の配列、そして@(0)
という特殊な配列のみ。 - それ以外の値(
0
以外の数値、空でない文字列、@(0)
以外の配列など)は、すべて$true
と評価される。 - この仕様は、とくに設定ファイルの値などを扱う際に便利だが、予期せぬ挙動に繋がるリスクも併せ持つ。
PowerShellにおけるbool
のキャストは強力な機能ですが、ルールを正しく理解してすることが、堅牢なスクリプトを書くための第一歩になるでしょう。本記事がその一助となれば幸いです。
参考情報
関連記事
Discussion
私も
bool
キャストは多用しているのですが、@{}
も@{}.Keys
も$true
になるのでなんだかなぁってかんじですねコメントありがとうございます。たしかにコメントで補足いただいた通りですね!
@{}
も@{}.Keys
も True対策としては、
Count
で数値化する感じでしょうか。(変換に変換を重ねてる……)
両方ともCountで数値に変換すると False
うーん、わかりにくい😁 ほんとうになんだかなぁって感じですね。
お返事ありがとうございます。それを見て、今更ながらに気づいたのですが、
Count
、Keys
などは get only なオブジェクトプロパティですが、ハッシュテーブルの格納データの参照キーとしても使えてしまうので、プロパティを参照しているつもりが格納データの参照になって結果も大変なことに。明示的に
$hash.Count = 値
とやることはないと思いますが、key-value ペアを貰ってきて$hash.$key = $value
とするようなときは要注意ですね。なんだかなぁ(2回目)ハッシュが空かどうかを知りたいだけなんですけどねぇ
良い方法がないか検証してみた結果、ハッシュテーブルはコレクションの中身を1つずつ取り出せる
.GetEnumerator()
メソッドで対応すると良さそうです。とくにハッシュテーブルは便利な分、いろいろ気をつけないといけないことが多そうですね。
補足いただきありがとうございます。勉強になりました。
👇 HashTable - .GetEnumerator()メソッドについて