🚩

[PowerShell]System.Booleanのデータ型に変換する際の注意事項まとめ

に公開4

概要

PowerShellでデータ型の変換(以降、キャスト)を行う際は 変換したいデータ型 + 変換対象の値(もしくは対象変数名) と定義することで実現可能です。

真偽であるSystem.Booleanbool)にキャストする際も同じ手法で対応可能なのですが、
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と評価されるのは、$null0、空文字列、空の配列、そして@(0)という特殊な配列のみ。
  • それ以外の値(0以外の数値、空でない文字列、@(0)以外の配列など)は、すべて$trueと評価される。
  • この仕様は、とくに設定ファイルの値などを扱う際に便利だが、予期せぬ挙動に繋がるリスクも併せ持つ。

PowerShellにおけるboolのキャストは強力な機能ですが、ルールを正しく理解してすることが、堅牢なスクリプトを書くための第一歩になるでしょう。本記事がその一助となれば幸いです。

参考情報

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_booleans

関連記事

https://haretokidoki-blog.com/pasocon_powershell-startup/
https://zenn.dev/haretokidoki/articles/7e6924ff0cc960
https://zenn.dev/haretokidoki/articles/fb6830f9155de5

GitHubで編集を提案

Discussion

npwshynpwshy

私も bool キャストは多用しているのですが、@{}@{}.Keys$true になるのでなんだかなぁってかんじですね

akiGAMEBOY५✍🤖はれときどきZennakiGAMEBOY५✍🤖はれときどきZenn

コメントありがとうございます。たしかにコメントで補足いただいた通りですね!

@{}@{}.Keys も True

HashTable も True
# @{} は True
PS C:\Users\XXXX> $hash = @{}
PS C:\Users\XXXX>
PS C:\Users\XXXX> $hash.GetType().FullName
System.Collections.Hashtable
PS C:\Users\XXXX>
PS C:\Users\XXXX> [bool]$hash
True
HashTable(Keys) も True
# @{}.Keys は True
PS C:\Users\XXXX> $hashKeys = @{}.Keys
PS C:\Users\XXXX> 
PS C:\Users\XXXX> $hashKeys.GetType().FullName
System.Collections.Hashtable+KeyCollection
PS C:\Users\XXXX>
PS C:\Users\XXXX> [bool]$hashKeys
True

対策としては、Countで数値化する感じでしょうか。
(変換に変換を重ねてる……)

両方ともCountで数値に変換すると False

HashTable + Count で False
# Countで数値
PS C:\Users\XXXX> $hash = @{}
PS C:\Users\XXXX>
PS C:\Users\XXXX> [bool]$hash.Count
False
PS C:\Users\XXXX>
HashTable(Keys) + Count で False
# Countで数値
PS C:\Users\XXXX> $hashKeys = @{}.Keys
PS C:\Users\XXXX>
PS C:\Users\XXXX> [bool]$hashKeys.Count
False
PS C:\Users\XXXX>

うーん、わかりにくい😁 ほんとうになんだかなぁって感じですね。

npwshynpwshy

お返事ありがとうございます。それを見て、今更ながらに気づいたのですが、

> $hash = @{}
> [bool]$hash.Count
False
> $hash.Name = 'Foo'
> [bool]$hash.Count
True
> $hash.Count = 0
> [bool]$hash.Count
False

CountKeys などは get only なオブジェクトプロパティですが、ハッシュテーブルの格納データの参照キーとしても使えてしまうので、プロパティを参照しているつもりが格納データの参照になって結果も大変なことに。

明示的に $hash.Count = 値 とやることはないと思いますが、key-value ペアを貰ってきて $hash.$key = $value とするようなときは要注意ですね。なんだかなぁ(2回目)

ハッシュが空かどうかを知りたいだけなんですけどねぇ

akiGAMEBOY५✍🤖はれときどきZennakiGAMEBOY५✍🤖はれときどきZenn

良い方法がないか検証してみた結果、ハッシュテーブルはコレクションの中身を1つずつ取り出せる.GetEnumerator()メソッドで対応すると良さそうです。

検証結果
# Key「Count」を含むハッシュテーブルを作成

PS C:\> $hash = @{
>>     Name  = 'Xxx'
>>     Count = 0                               # 'Count'というキーに   0 を格納
>>     Keys  = 0                               # 'Keys' というキーにも 0 を格納
>> }

# ❌ 意図しない挙動($hash.Count)
#    共有して頂いたケース
PS C:\> $hash.Count
0

PS C:\> [bool]$hash.Count                           # 'Count'キーの値である 0 を見てしまい「False」と誤判定
False

# ❌ 意図しない挙動(.Keys.Count)          
#    つぎに思いつき'Keys'プロパティを参照してくれると思いましたが、ダメでした
PS C:\> $hash.Keys                                  # キー'Keys'の値 0 が返る('Keys'プロパティではない)
0

PS C:\> $hash.Keys.Count                            # キー'Keys'の値 0 を評価し、Count 1 で返す('Keys'プロパティのカウントにはならなかった)
1

PS C:\> [bool]$hash.Keys.Count                      # 結果的には True となるが、ロジックが破綻
True

# ✅ 期待する結果(GetEnumerator)
#    .GetEnumerator() メソッドを配列でまとめてカウントする方法で解決
PS C:\> $hash.GetEnumerator()                       # .GetEnumerator() メソッドはキー名と衝突しない

Name                           Value
----                           -----
Count                          0
Name                           Xxx
Keys                           0

PS C:\> ($hash.GetEnumerator()).Count               # そのまま指定すると取り出した1要素ずつしかカウントされない
1
1
1

PS C:\> @($hash.GetEnumerator()).Count              # 配列にまとめてからカウントして値が 3 に
3

PS C:\> [bool]@($hash.GetEnumerator()).Count        # 要素数 3 で評価し正しく True と判定
True

# --- 他の方への参考情報 ---
# ✅ ハッシュテーブルでキーの参照方法(キーが Keys の場合)
PS C:\> $hash['Keys']                               # キー'Keys'に格納された値(Value)を参照
0
PS C:\> $hash['Keys'].Count                         # キー'Keys'に格納された値の.Countプロパティを取得
1

とくにハッシュテーブルは便利な分、いろいろ気をつけないといけないことが多そうですね。
補足いただきありがとうございます。勉強になりました。

👇 HashTable - .GetEnumerator()メソッドについて
https://learn.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-hashtable#getenumerator