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を見てみましょう。
$ScriptTable.Double
入っていますね。
$_ * $_

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

例えば[int]を入れてみると
$ScriptTable.ID = 4
ちゃんと型制限にかかりましたね。
null 値の式ではメソッドを呼び出せません。
発生場所 D:\Document\Program\Powershell\TypedHashtable\TypedHashtable.ps1:20 文字:1
+ $emploeetyped.Name.GetType().FullName
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	+ CategoryInfo          : InvalidOperation: (:) []、RuntimeException
	+ FullyQualifiedErrorId : InvokeMethodOnNull
勿論[string]もだめです。
$ScriptTable.Name = "John"
想定通りです。
このオブジェクトにプロパティ 'ID' が見つかりません。プロパティが存在し、設定可能なことを確認してください。
発生場所 D:\Document\Program\Powershell\TypedHashtable\TypedHashtable.ps1:27 文字:1
+ $ScriptTable.ID = 4
+ ~~~~~~~~~~~~~~~~~~~
	+ CategoryInfo          : InvalidOperation: (:) []、RuntimeException
	+ FullyQualifiedErrorId : PropertyAssignmentException

抜け道

ScriptBlockは、ようは{}なので…こんな抜け道があります。 [int]を入れるなら
$ScriptTable.ScriptInt = {4}
[string]を入れるなら
$ScriptTable.ScriptStr = {"abc"}
型を確認しましょう。
$ScriptTable.ScriptInt.GetType().fullName
ScriptBlockですね。
System.Management.Automation.ScriptBlock

HashtableにいれたScriptBlockの実行方法

まずは、作ったHashtableを確認しましょう。
$ScriptTable
想定通りですね。
Key       Value
---       -----
Double    $_ * $_
Triple    $_ * $_ * $_
ScriptInt 4
ScriptStr "abc"

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

単純に、& (アンパサダント) で実行できます。
&$ScriptTable.ScriptInt
&$ScriptTable.ScriptStr
出力結果です。
4
abc
あるいは、& (アンパサダント)ではなく . (ドット化)でも実行できます。
.$ScriptTable.ScriptInt
.$ScriptTable.ScriptStr
出力結果です。
4
abc
そして値自体は型がそれぞれ割り当てられています。
System.Int32
System.String

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

自動変数$_を使ってますので、| を介してHashtableに入れたScriptBlockに渡します。 正常に実行できない方法 当然この場合、先ほどのやり方では実行できず、エラーも出ませんが、出力もでません。
2 | &$ScriptTable.Double
2 | .$ScriptTable.Double
これもダメなのはお分かりの通りです。
2 | &{$ScriptTable.Double}
2 | .{$ScriptTable.Double}
これでは、ScriptBlockの記述が出るだけです。
$_ * $_
$_ * $_
正常に実行できる方法 ならどうするのか、自動変数が生成されないのが理由なので、自動変数に渡せるやり方を使えばいいわけです。 つまりForeach-Obejctで渡します。
2 | %{& $ScriptTable.Double}
2 | %{& $ScriptTable.Triple}
でましたね。
4
8
あるいは、& (アンパサダント)ではなく . (ドット化)でも実行できます。
2 | %{. $ScriptTable.Double}
2 | %{. $ScriptTable.Triple}
でましたね。
4
8
※補足 $_が必要なので、Invoke-Commandで渡そうとするとダメになります。
2 | %{Invoke-Command {$ScriptTable.Double}}
ScriptBlockが出力されてしまいます。
$_ * $_

paramを使ったScriptBlockの実行方法

もっと使いやすい方法として、paramを利用しましょう。 まずは、paramを含むScriptBlockをHashtableにいれます。
$ScriptTable.QuadroParam = {param([int]$num); $num * $num * $num * $num}
実行するには、単純に、& (アンパサダント) をつけます。
& $ScriptTable.QuadroParam -num 2
出ましたね。
8
あるいは、& (アンパサダント)ではなく . (ドット化)でも実行できます。
. $ScriptTable.QuadroParam -num 2
出ましたね。
8
さらに、Invoke-Commandでもいいでしょう。
Invoke-Command {& $ScriptTable.QuadroParam -num 2}
でました。
8

まとめ

簡単なScriptBlockをまとめておいて分類するのに便利かなとか思ったのです。 しかし、ScriptBlockはインテリセンスが効かないのが辛いですね、paramである-nunを呼び出そうにも知らないと実行できないのがちょっと。 使わなさそうな気がしてきましたorz