🔍

[PowerShell]自動変数(予約語)のデータ型を調べてみた

2023/08/23に公開

概要

PowerShellの予約語のようなものである自動変数を使用する際、
正しくデータ型を理解しておく必要があります。

私は、PowerShellのスクリプトを作成し自動変数を使用する度にコマンドでデータ型をチェックしていました。
いっその事、事前にすべての自動変数のデータ型を調べて一覧化してしまおうというのが、この記事の趣旨となります。

この記事のターゲット

  • PowerShellユーザーの方
  • 自動変数のデータ型を確認したい方

環境

PowerShellのバージョン
PS C:\WINDOWS\system32> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.19041.3031
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.19041.3031
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\WINDOWS\system32>

なお、一部の自動変数はPowerShell Coreを導入しチェックする必要があった為、
ZIP版をローカルにダウンロードして検証しました。
念のため、PowerShell Coreのバージョンを記載しておきます。

PowerShell Coreのバージョン
PS D:\Downloads\PowerShell-7.3.6-win-x64> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.3.6
PSEdition                      Core
GitCommitId                    7.3.6
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

PS D:\Downloads\PowerShell-7.3.6-win-x64>

調査方法

自動変数名.GetType().FullNameによりデータ型を調べました。
実際に、$PSVersionTableのデータ型を調べた際のコマンドは、下記の通りです。

コピー用
$PSVersionTable.GetType().FullName
$PSVersionTableのデータ型を確認した時
PS C:\WINDOWS\system32> $PSVersionTable.GetType().FullName
System.Collections.Hashtable
PS C:\WINDOWS\system32>
知っておくと便利な情報:「Get-Member」コマンドでプロパティを調べる < クリックで折りたたみが開く >

パイプラインでコマンドを渡してGet-Memberコマンドを実行すると、
使用できるプロパティの一覧が表示できる。

コピー用
$PSVersionTable | Get-Member
$PSVersionTableでGet-Memberを実行した場合
PS C:\WINDOWS\system32> $PSVersionTable | Get-Member


   TypeName: System.Collections.Hashtable

Name              MemberType            Definition
----              ----------            ----------
Add               Method                void Add(System.Object key, System.Object value), void IDictionary.Add(Syste...
Clear             Method                void Clear(), void IDictionary.Clear()
Clone             Method                System.Object Clone(), System.Object ICloneable.Clone()
Contains          Method                bool Contains(System.Object key), bool IDictionary.Contains(System.Object key)
ContainsKey       Method                bool ContainsKey(System.Object key)
ContainsValue     Method                bool ContainsValue(System.Object value)
CopyTo            Method                void CopyTo(array array, int arrayIndex), void ICollection.CopyTo(array arra...
Equals            Method                bool Equals(System.Object obj)
GetEnumerator     Method                System.Collections.IDictionaryEnumerator GetEnumerator(), System.Collections...
GetHashCode       Method                int GetHashCode()
GetObjectData     Method                void GetObjectData(System.Runtime.Serialization.SerializationInfo info, Syst...
GetType           Method                type GetType()
OnDeserialization Method                void OnDeserialization(System.Object sender), void IDeserializationCallback....
Remove            Method                void Remove(System.Object key), void IDictionary.Remove(System.Object key)
ToString          Method                string ToString()
Item              ParameterizedProperty System.Object Item(System.Object key) {get;set;}
Count             Property              int Count {get;}
IsFixedSize       Property              bool IsFixedSize {get;}
IsReadOnly        Property              bool IsReadOnly {get;}
IsSynchronized    Property              bool IsSynchronized {get;}
Keys              Property              System.Collections.ICollection Keys {get;}
SyncRoot          Property              System.Object SyncRoot {get;}
Values            Property              System.Collections.ICollection Values {get;}


PS C:\WINDOWS\system32>

参考情報:調査方法

https://learn.microsoft.com/ja-jp/dotnet/api/system.object.gettype?view=net-7.0
https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_properties?view=powershell-7.3

調査結果

コマンド結果でnullによりエラーとなったものに対しては、「エラー(nullだった)」と表記。
初期値がnullの自動変数は値が入る条件(たとえば、$_ではForEach内で値がセット)がある為、
PowerShellの起動直後ではnullになっているものと思われます。

現時点で理由がわかるものに関しては、その理由を注釈として記載しています。

参考:「 $_ 」のデータ型を確認した際のエラー
PS C:\WINDOWS\system32> $_.GetType().FullName
null 値の式ではメソッドを呼び出せません。
発生場所 行:1 文字:1
+ $_.GetType().FullName
+ ~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

PS C:\WINDOWS\system32>
自動変数名 データ型
$$ System.String
$? System.Boolean
$^ System.String
$_($PSItem)
* パイプラインで渡ってくるオブジェクトにより変化
Get-Process | ForEach-Object -Process { Write-Host $_.GetType().FullName } の場合だと System.Diagnostics.Process
* 初期起動時に確認するとエラー(nullだった)
$args System.Object[]
$ConsoleFileName System.String
$EnabledExperimentalFeatures
* PowerShell Core 導入で使用可能
System.Management.Automation.Internal.ReadOnlyBag`1[[System.String, System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
* Windows標準のPowerShellだとエラー(nullだった)
$Error System.Collections.ArrayList
$ErrorActionPreference System.Management.Automation.ActionPreference
$Event 実行環境により変化
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$EventArgs 実行環境により変化
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$EventSubscriber 実行環境により変化
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$ExecutionContext System.Management.Automation.EngineIntrinsics
$false System.Boolean
$foreach System.Array+SZArrayEnumerator
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$HOME System.String
$Host System.Management.Automation.Internal.Host.InternalHost
$input System.Collections.ArrayList+ArrayListEnumeratorSimple
$IsCoreCLR
* PowerShell Core 導入で使用可能
System.Boolean
* Windows標準のPowerShellだとエラー(nullだった)
$IsLinux
* PowerShell Core 導入で使用可能
System.Boolean
* Windows標準のPowerShellだとエラー(nullだった)
$IsMacOS
* PowerShell Core 導入で使用可能
System.Boolean
* Windows標準のPowerShellだとエラー(nullだった)
$IsWindows
* PowerShell Core 導入で使用可能
System.Boolean
* Windows標準のPowerShellだとエラー(nullだった)
$OutputEncoding System.Text.ASCIIEncoding
$Matches System.Collections.Hashtable
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$MyInvocation System.Management.Automation.InvocationInfo
$NestedPromptLevel System.Int32
$null エラー(nullのため)
$PID System.Int32
$profile System.String
$PSBoundParameters System.Management.Automation.PSBoundParametersDictionary
$PSCmdlet System.Management.Automation.PSScriptCmdlet
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$PSCommandPath System.String
$PSCulture System.String
$PSDebugContext System.Management.Automation.PSDebugContext
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$PSEdition System.String
$PSHOME System.String
$PSScriptRoot System.String
$PSSenderInfo System.Management.Automation.Remoting.PSSenderInfo
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$PSUICulture System.String
$PSVersionTable System.Collections.Hashtable
$PWD System.Management.Automation.PathInfo
$Sender 実行環境により変化
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$ShellId System.String
$StackTrace System.String
$switch System.Array+SZArrayEnumerator
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$this 実行環境により変化
* 参考:調査時のエビデンス
* 初期起動時に確認するとエラー(nullだった)
$true System.Boolean

調査時のエビデンス

$Event と $Sender 、 $this のデータ型をチェック < クリックで折りたたみが開く >

処理内容によって変化する。下記のサンプルコードでは、

  • $Eventが
    [System.Windows.Forms.MouseEventArgs]
  • $Senderが
    [System.Windows.Forms.Button]
  • $thisが
    [System.Windows.Forms.Button]
$Event と $Sender と $this のデータ型をチェックするコード
[Void][Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$form   = New-Object Windows.Forms.Form
$button = New-Object Windows.Forms.Button
$button.Text = "テストのボタン"
$button.Dock = "Fill"
$button_click =
{
    ($Sender, $Event) = $this, $_
    
    Write-Host ' ----- '
    Write-Host 'Event : '$Event.GetType().FullName
    Write-Host 'Sender: '$Sender.GetType().FullName
    Write-Host 'this  : '$this.GetType().FullName
    Write-Host ' ----- '
}
$button.add_Click($button_click)
$form.Controls.Add($button)
$form.ShowDialog()

上記は実行後、ボタンを押すとデータ型を確認可能。

$EventArgs のデータ型をチェック < クリックで折りたたみが開く >

処理内容によって変化する。下記のサンプルコードでは、

  • $EventArgsが
    [System.IO.FileSystemEventArgs]
$EventArgs のデータ型をチェックしたコード
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "C:\"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
$watcher.Site
$arrary = @()

$watcher.Filter = ""

Register-ObjectEvent $watcher "Created" -Action {
    Write-Host $EventArgs.GetType().FullName
    exit
}

上記を実行するとデータ型がチェックできる。

$Event と $EventSubscriber 、 $Sender のデータ型をチェック < クリックで折りたたみが開く >

処理内容によって変化する。下記のサンプルコードでは、

  • $Eventが
    [System.Management.Automation.PSEventArgs]
  • $EventSubscriberが
    [System.Management.Automation.PSEventSubscriber]
  • $Senderが
    [System.IO.FileSystemWatcher]
ファイル監視のコード
$wait= New-Object System.IO.FileSystemWatcher

$wait.NotifyFilter= [IO.NotifyFilters]::LastWrite
$wait.Path= "D:\Downloads"
$wait.Filter= "*.txt”

#バックグランドで稼働

Register-ObjectEvent -InputObject $wait -SourceIdentifier "mml_wch” -EventName "Changed” -Action{

    if($i -eq $null){ # 初期化

        [int]$i= 0
    }

    $t= $Event.TimeGenerated # 見やすくするため
    $f= $Event.SourceEventArgs

    [string]$lated_time= $t.ToString(‘yy/MM/dd HH:mm:ss.f’)

    if($lated_time -ne $chk_time){ # 出力

        Write-Host ("`r`n”+ ‘no.+ $i)
        $i++

        write-host ("change type: "+ $f.ChangeType)
        write-host ("full path: "+ $f.FullPath)
        write-host ("name: "+ $f.Name)
        write-host ("time: "+ $lated_time)
        
        write-host ("-----")
        write-host ($Event                 : ‘+ $Event.GetType().FullName)
        write-host ($EventSubscriber       : ‘+ $EventSubscriber.GetType().FullName)
        write-host ($Sender                : ‘+ $Sender.GetType().FullName)
        # write-host (‘$SourceEventArgs       : ‘+ $SourceEventArgs.GetType().FullName)
        # write-host (‘$SourceArgs            : ‘+ $SourceArgs.GetType().FullName)
        write-host ($Event.SourceArgs      : ‘+ $Event.SourceArgs.GetType().FullName)
        write-host ($Event.SourceEventArgs : ‘+ $Event.SourceEventArgs.GetType().FullName)
        write-host ("-----")

        [string]$chk_time= $lated_time
    }
}

上記を実行すると「D:¥Downloads配下」のテキストファイルを監視する常駐プロセスが起動する。

常駐プロセス起動中に「D
PS C:\WINDOWS\system32>
no. 0
change type: Changed
full path: D:\Downloads\20230823_memo.txt
name: 20230823_memo.txt
time: 23/08/23 15:48:13.2
-----
$Event                 : System.Management.Automation.PSEventArgs
$EventSubscriber       : System.Management.Automation.PSEventSubscriber
$Sender                : System.IO.FileSystemWatcher
$Event.SourceArgs      : System.Object[]
$Event.SourceEventArgs : System.IO.FileSystemEventArgs
-----

PS C:\WINDOWS\system32>
起動中の常駐プロセスを停止する方法
# 監視終了
Unregister-Event -SourceIdentifier "mml_wch"
$foreach のデータ型をチェック < クリックで折りたたみが開く >
  • $foreachが
    [System.Array+SZArrayEnumerator]
$foreach のデータ型をチェックしたコード
foreach($item in $array) {
	write-host "foreach: $($foreach.GetType().FullName)"
	write-host "item   : $($item.GetType().FullName)"
	break
}
実際に実行した結果
PS C:\WINDOWS\system32> foreach($item in $array) {
>>     write-host "foreach: $($foreach.GetType().FullName)"
>>     write-host "item   : $($item.GetType().FullName)"
>>     break
>> }
foreach: System.Array+SZArrayEnumerator
item   : System.Int32
PS C:\WINDOWS\system32>
$Matches のデータ型をチェック < クリックで折りたたみが開く >
  • $Matchesが
    [System.Collections.Hashtable]
$Matches のデータ型をチェックしたコマンド結果
# マッチした場合
PS C:\WINDOWS\system32> "abc" -match "abc"
True
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $Matches

Name                           Value
----                           -----
0                              abc


PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $Matches.Gettype().Fullname
System.Collections.Hashtable
PS C:\WINDOWS\system32>
# マッチしない場合
PS C:\WINDOWS\system32> "abc" -match "123"
False
PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $Matches

Name                           Value
----                           -----
0                              abc


PS C:\WINDOWS\system32>
PS C:\WINDOWS\system32> $Matches.Gettype().Fullname
System.Collections.Hashtable
PS C:\WINDOWS\system32>
$PSCmdlet のデータ型をチェック < クリックで折りたたみが開く >
  • $PSCmdletが
    [System.Management.Automation.PSScriptCmdlet]
$PSCmdlet のデータ型をチェックしたコード
function typeof-psCmdlet {
  [cmdletBinding()] param()

   echo "type of `$psCmdlet is $($psCmdlet.GetType().FullName)"
}

実際に実行した結果
PS C:\WINDOWS\system32> function typeof-psCmdlet {
>>   [cmdletBinding()] param()
>>
>>    echo "type of `$psCmdlet is $($psCmdlet.GetType().FullName)"
>> }
>>
>> typeof-psCmdlet
type of $psCmdlet is System.Management.Automation.PSScriptCmdlet
PS C:\WINDOWS\system32>
$PSDebugContext のデータ型をチェック < クリックで折りたたみが開く >
  • $PSDebugContextが
    []System.Management.Automation.PSDebugContext]
VS Codeを使いブレークポイントで停めた状態でデータ型をチェックした時
$PSDebugContext.GetType().FullName              # ← この行をブレークポイントに設定しデバッグ実行
Write-Host $PSDebugContext.GetType().FullName   # ← デバッグや通常の実行ではnullエラー(*)となってしまう。
                                                #   ブレークポイントでデバッグ停止中に同じコマンドを叩くと確認できた
                                                #   * nullエラー:「null 値の式ではメソッドを呼び出せません。」

調査時に当初は下記のコードを使ってデバッグしていない通常起動の時は、"Is Null"。
デバッグ中の時は、"Is not Null" + $PSDebugContextのデータ型を表示。という想定でいたが、
期待通りに動作しなく、どちらのパターンでもTrue系となった。
原因は不明だが、おそらく$PSDebugContextがデバッグのブレーク中でないと、データが参照できないのだと思われる。

検証に失敗したコード
if ($null -eq $PSDebugContext) {
    Write-Host "Is Null"
} else {
    Write-Host "Is not Null"
    Write-Host $PSDebugContext.GetType().FullName
}
$PSSenderInfo のデータ型をチェック < クリックで折りたたみが開く >
  • $PSSenderInfoが
    [System.Management.Automation.Remoting.PSSenderInfo]
$PSSenderInfo のデータ型をチェックしたコマンド結果
PS C:\WINDOWS\system32> Enter-PSSession -ComputerName 192.168.XXX.XXX -Credential "UserName"
[192.168.XXX.XXX]: PS D:\ドキュメント>
[192.168.XXX.XXX]: PS D:\ドキュメント> $PSSenderInfo


UserInfo             : System.Management.Automation.Remoting.PSPrincipal
ClientTimeZone       : System.CurrentSystemTimeZone
ConnectionString     : http://192.168.XXX.XXX:5985/wsman?PSVersion=5.1.19041.3031
ApplicationArguments : {PSVersionTable}
ConfigurationName    : Microsoft.PowerShell
ConnectedUser        : ComputerName\UserName
RunAsUser            : ComputerName\UserName



[192.168.XXX.XXX]: PS D:\ドキュメント>
[192.168.XXX.XXX]: PS D:\ドキュメント> $PSSenderInfo.GetType().FullName
System.Management.Automation.Remoting.PSSenderInfo
[192.168.XXX.XXX]: PS D:\ドキュメント>
[192.168.XXX.XXX]: PS D:\ドキュメント> exit
PS C:\WINDOWS\system32>
$switch のデータ型をチェック < クリックで折りたたみが開く >
  • $foreachが
    [System.Array+SZArrayEnumerator]
$PSCmdlet のデータ型をチェックしたコード
switch (2) {
    1 {"One."}
    2 {$switch.GetType().FullName}
    3 {"Three."}
    default {"Not matched."}
}
実際に実行した結果
PS C:\WINDOWS\system32> switch (2) {
>>     1 {"One."}
>>     2 {$switch.GetType().FullName}
>>     3 {"Three."}
>>     default {"Not matched."}
>> }
System.Array+SZArrayEnumerator
PS C:\WINDOWS\system32>

参考情報:本記事を作成するにあたり参考にした情報

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_automatic_variables?view=powershell-7.3
https://tex2e.github.io/blog/powershell/automatic-variables

関連記事

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

GitHubで編集を提案

Discussion