目次

概要

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#と諸々])。

RunspaceInvoke

.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# 側に返されます。

PowerShell 側での引数の受け取り方

前述のとおり、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 は列挙子(IEnumerator) であって、リストや配列ではないので、 以下のような値の受け取り方はできません。

$arg1 = $input[0]   # エラー。[] が使えない。
$arg2 = $input[1]   # 同上。
$arg1, $arg2 = $input   # エラー。この構文も、右辺がリストでないと使えない。

ちょっとうざったいですが、以下のいずれかのような受け取り方をするのがてっとり早いと思います。

$count = 0
foreach($x in $input)
{
  switch ($count)
  {
    0 { $arg1 = $x }
    1 { $arg2 = $x }
  }
  $count++
}
if ($input.MoveNext()) { $arg1 = $input.Current }
if ($input.MoveNext()) { $arg2 = $input.Current }

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

更新履歴

ブログ