首页 > 解决方案 > 无法将 .Net 中的对象通过管道传输到托管的 powershell

问题描述

我想Powershell在应用程序中执行脚本C#。为此,我正在使用Microsoft.PowerShell.5.ReferenceAssemblies包(由于net48要求)。

我想将对象从管道.Net传输到脚本但没有成功。

        static void Main(string[] args)
        {
            var runspace = RunspaceFactory.CreateRunspace();
            runspace.Open();

            var ps = PowerShell.Create();
            ps.Runspace = runspace;

            var pipeline = runspace.CreatePipeline();
            pipeline.Commands.AddScript(@"example.ps1");

            pipeline.Input.Write("1");
            pipeline.Input.Write("2");
            pipeline.Input.Flush();
            
            var res = pipeline.Invoke();
            foreach (var xx in res) {
                Console.WriteLine(xx);
            }
        }

示例.ps1

[CmdletBinding()]
param (
    [Parameter(ValueFromPipeline=$true)]
    $p
)

Begin {
    Write-Output "Start"
}
    
Process {
    Write-Output "Process $_"
}
    
End {
    Write-Output "End"
}

实际输出:

Start
Process
End

预期输出:

Start
Process 1
Process 2
End

标签: c#powershell

解决方案


经过大量的经验测试,我发现以下对我有用:

pipeline.Input.Write("1");
pipeline.Input.Write("2");
pipeline.Commands.Add(".\\example.ps1");

也就是说,使用pipeline.Commands.Add,而不是pipeline.Commands.AddScript。即使您的命令是脚本文件的名称,它也是命令而不是脚本!

另外,请注意,在pipeline.Input.Write之前pipeline.Commands.Add,我已经删除了pipeline.Input.Flush()- 这些似乎都以不同的方式破坏了事物。

完整代码变为:

static void Main(string[] args)
{

    var runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();

    var ps = PowerShell.Create();
    ps.Runspace = runspace;

    var pipeline = runspace.CreatePipeline();

    pipeline.Input.Write("1");
    pipeline.Input.Write("2");
    pipeline.Commands.Add(".\\example.ps1");

    var res = pipeline.Invoke();
    foreach (var xx in res) {
        Console.WriteLine(xx);
    }

}

应用程序将其写入控制台:

Start
Process 1
Process 2
End

推论是,它看起来AddScript不消耗管道输入。

为了完整起见,这是我在此过程中所做的其他一些(损坏的)测试:

// fails - original code
//pipeline.Commands.AddScript(".\\example.ps1");
//pipeline.Input.Write("1");
//pipeline.Input.Write("2");
//pipeline.Input.Flush();
// Start
// Process
// End

// test - works for a command, but requires in-script parameters
//pipeline.Commands.AddScript("1, 2 | write-output");
// 1
// 2

// test - works for a script, but requires in-script parameters
//pipeline.Commands.AddScript("1, 2 | .\\example.ps1");
// Start
// Process 1
// Process 2
// End

// test - works for a command with pipeline input
//pipeline.Input.Write("1");
//pipeline.Input.Write("2");
//pipeline.Commands.Add("write-output");
// 1
// 2

// test - fails with AddScript and mandatory parameters on script
//pipeline.Input.Write("1");
//pipeline.Input.Write("2");
//pipeline.Commands.AddScript("write-output");
// System.Management.Automation.ParameterBindingException: 'Cannot process command because of one
// or more missing mandatory parameters: InputObject.'

// test - fails with AddScript and optional parameters on script
//pipeline.Input.Write("1");
//pipeline.Input.Write("2");
//pipeline.Commands.AddScript(".\\example.ps1");
// Start
// Process
// End

// fails - flush kills pipelne
//pipeline.Input.Write("1");
//pipeline.Input.Write("2");
//pipeline.Input.Flush();
//pipeline.Commands.Add(".\\example.ps1");
// Start
// Process
// End

// fails - pipeline written after script
//pipeline.Commands.AddScript(".\\example.ps1");
//pipeline.Input.Write("1");
//pipeline.Input.Write("2");
// Start
// Process
// End

更新

您可以将包含 PowerShell 脚本的字符串转换为“可流水线”命令,如下所示(注意new Command- ie中的第二个参数isScript: true):

pipeline.Input.Write("1");
pipeline.Input.Write("2");

var scriptText = File.ReadAllText(".\\example.ps1");
pipeline.Commands.Add(
    new Command(scriptText, true)
);

// Start
// Process 1
// Process 2
// End

推荐阅读