C# 上で PowerShell スクリプトを実行する方法を説明します。 その際、C# から PowerShell に引数を渡し、 PowerShell からの戻り値を C# で受け取る方法も説明します。
PowerShell の機能を .NET 言語から利用するためには、 System.Management.Automation.dll を参照する必要があります。 この DLL は、Windows SDK をインストールすると、Program Files の下にある以下のパスに配置されます。
%PROGRAMFILES%\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0
あるいは、PowerShell だけインストールして、GAC(Global Assembly Cache)から取りだす方法もあるようです (参考: コマンドレットの作成方法 [C#と諸々])。
.NET 言語から PowerShell スクリプトを実行するには System.Management.Automation.RunspaceInvoke クラスを使います。
using (var invoker = new RunspaceInvoke()) { var results = invoker.Invoke(source, new object[] { }); foreach (var result in results) { Console.Write(result); } }
Invoke メソッドの第2引数は、パイプライン入力としてスクリプトに渡されます。 同様に、スクリプト中でパイプラインに出力した結果が results として C# 側に返されます。
前述のとおり、C# から与えられた入力はパイプライン入力になるので、 PowerShell 側では $input 自動変数を使って受け取ることができます。
例えば、以下のコードでは、パイプラインで与えられた入力を二乗して出力します。
using System; using System.Management.Automation; static void Main() { string source = @"foreach($x in $input) { $x * $x }"; using (var invoker = new RunspaceInvoke()) { var result = invoker.Invoke(source, new[] { 1, 2, 3, 4 }); foreach (var r in result) { Console.WriteLine(r); } } }
1 4 9 16
要するに、これで以下のような PowerShell コマンドと同じような実行結果になります。
> $source = { foreach($x in $input) { $x * $x } } > $results = 1, 2, 3, 4 | & $source > $results 1 4 9 16
ちなみに、$input は IEnumerable ではあるけども、リストや配列ではないので、 以下のような値の受け取り方はできません。
$arg1 = $input[0] # エラー。[] が使えない。 $arg2 = $input[1] # 同上。
$arg1, $arg2 = $input # エラー。この構文も、右辺がリストでないと使えない。
ちょっとうざったいですが、以下のような受け取り方をするのがてっとり早いと思います。
$count = 0
foreach($x in $input)
{
switch ($count)
{
0 { $arg1 = $x }
1 { $arg2 = $x }
}
$count++
}
LINQ の ToList を PowerShell からも使いたい・・・
2つの配列の要素ごとの積を求めます。
using System; using System.Management.Automation; static void Main() { string source = @" $count = 0 foreach($a in $input) { switch ($count) { 0 { $lhs = $a } 1 { $rhs = $a } } $count++ } $len = [Math]::Min($lhs.Length, $rhs.Length) for($i = 0; $i -lt $len; $i++) { $lhs[$i] * $rhs[$i] } "; using (var invoker = new RunspaceInvoke()) { var lhs = new[] { 1, 2, 3, 4, 5 }; var rhs = new[] { 2, 3, 4, 5, 6 }; var result = invoker.Invoke(source, new[] { lhs, rhs }); foreach (var r in result) { Console.WriteLine(r); } } }
2 6 12 20 30