tech.guitarrapc.cóm

Technical updates

PowerShellのHashtableにScriptBlockを保持して呼び出す

さて、名前付きのScriptBlockといえばfunctionやfilterです。 ただ、こういったやり方もあるのかなということで、ScriptBlockをHashtableに保持して任意に使う方法も考えてみました。

SQLのProcedureのように、記述内容がぱっと確認できるイメージです。 これならHashtable毎に分類もできていいかも! という単純な発想で。

ただ、当然書き換えもできるので、自由過ぎる気もしますが…Tipsということで。

ScriptBlockに制限したHashtableを作る

せっかくScriptBlockに制限したいので、前回の記事で紹介した型安全(type-safe)なHashtableを利用します。 ※要はDictionaryです。が、ここではHashtableとしています。分かりにくくてすいません。

PowerShellでHashtableを型付けしたい

このようなコードでサクッと。

$ScriptTable = New-Object 'System.Collections.Generic.Dictionary[string, ScriptBlock]'

ScriptBlockを入れる

では、二乗するScriptBlockと三乗するScriptBlockを入れてみましょう。

$ScriptTable.Double = {$_ * $_}
$ScriptTable.Triple = {$_ * $_ * $_}

Valueを見てみましょう。

PS> $ScriptTable.Double
$_ * $_

ScriptBlock型でないものを入れようとするとどうなるの?

例えば数値を入れてみると、ちゃんと型制限にかかりましたね。

PS> $ScriptTable.ID = 4
null 値の式ではメソッドを呼び出せません。
発生場所 D:\Document\Program\Powershell\TypedHashtable\TypedHashtable.ps1:20 文字:1
+ $emploeetyped.Name.GetType().FullName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

もちろん文字列もだめです。

PS> $ScriptTable.Name = "John"
このオブジェクトにプロパティ 'ID' が見つかりません。プロパティが存在し、設定可能なことを確認してください。
発生場所 D:\Document\Program\Powershell\TypedHashtable\TypedHashtable.ps1:27 文字:1
+ $ScriptTable.ID = 4
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) []、RuntimeException
    + FullyQualifiedErrorId : PropertyAssignmentException

抜け道

ScriptBlockは、ようは{}なので…こんな抜け道があります。数値や文字列を入れるならこう。

PS> $ScriptTable.ScriptInt = {4}
PS> $ScriptTable.ScriptStr = {"abc"}

型を確認しましょう。

PS> $ScriptTable.ScriptInt.GetType().fullName
System.Management.Automation.ScriptBlock

HashtableにいれたScriptBlockの実行方法

まずは、作ったHashtableを確認しましょう。

PS> $ScriptTable
Key       Value
---       -----
Double    $_ * $_
Triple    $_ * $_ * $_
ScriptInt 4
ScriptStr "abc"

自動変数を使わないScriptBlockの実行方法

単純に、&(アンパサダント) で実行できます。

PS> &$ScriptTable.ScriptInt
4
PS> &$ScriptTable.ScriptStr
abc

あるいは、& (アンパサダント)ではなく . (ドット化)でも実行できます。

PS> .$ScriptTable.ScriptInt
4
PS> .$ScriptTable.ScriptStr
abc

そして値自体は型がそれぞれ割り当てられています。

System.Int32
System.String

自動変数を使ったScriptBlockの実行方法

自動変数$_を使ってますので、| を介してHashtableに入れたScriptBlockへ渡します。

正常に実行できない方法

当然この場合、先ほどのやり方では実行できず、エラーも出ませんが、出力もでません。

2 | &$ScriptTable.Double
2 | .$ScriptTable.Double

これでは、ScriptBlockの記述が出るだけです。

PS> 2 | &{$ScriptTable.Double}
$_ * $_
PS> 2 | .{$ScriptTable.Double}
$_ * $_

正常に実行できる方法

ならどうするのか。自動変数が生成されないのは理由なので、自動変数に渡せるやり方を使えばいいわけです。 つまりForeach-Obejctで渡します。

PS> 2 | %{& $ScriptTable.Double}
4
PS> 2 | %{& $ScriptTable.Triple}
8

あるいは、&(アンパサダント)ではなく.(ドット化)でも実行できます。

PS> 2 | %{. $ScriptTable.Double}
4
PS> 2 | %{. $ScriptTable.Triple}
8

※補足: $_が必要なので、Invoke-Commandで渡そうとするとScriptBlockが出力されてしまいます。

PS> 2 | %{Invoke-Command {$ScriptTable.Double}}
$_ * $_

paramを使ったScriptBlockの実行方法

もっと使いやすい方法として、paramを利用しましょう。 まずは、paramを含むScriptBlockをHashtableにいれます。

$ScriptTable.QuadroParam = {param([int]$num); $num * $num * $num * $num}

実行するには、単純に、& (アンパサダント) をつけます。

PS> & $ScriptTable.QuadroParam -num 2
8

あるいは、& (アンパサダント)ではなく . (ドット化)でも実行できます。

PS> . $ScriptTable.QuadroParam -num 2
8

さらに、Invoke-Commandでもいいでしょう。

PS> Invoke-Command {& $ScriptTable.QuadroParam -num 2}
8

まとめ

簡単なScriptBlockをまとめておいて分類するのに便利かなとか思ったのです。

しかし、ScriptBlockはインテリセンスが効かないの辛いですね。paramである-nunを呼び出そうにも実行できないのがちょっと。

使わなさそうな気がしてきました。