首页 > 解决方案 > 如何在 Powershell 和 c# 之间传递标志变量?

问题描述

我有一个脚本,它使用一个包含 c# 代码的脚本块来启动一个 powershell 作业。

现在我有一个 sleep 命令可以让代码正确定位自己,但是更简洁的方法(如果可能的话)是让 psjob c# 代码设置一个标志并允许我的其余代码尽快启动它可以而不是等待 5 秒睡眠作为一个包罗万象。这可能吗?

$containerScript = {a mix of c# and powershell}
Start-Job -ScriptBlock $containerScript ;
sleep 5; # wait here for c# code to set up
Connect-MsolService;

因此,如果我在 c# psjob 脚本中设置了一个变量,bool started = true;说明我如何使用循环而不是sleep 5在上面显示的 powershell 代码中读取它,这在 psjob 运行空间和 c# 代码之外?

标签: c#powershellenvironment-variablesglobal-variables

解决方案


您正在寻找的是一种自定义 .NET 类型的方法来异步写入 PowerShell 的输出流,以在方法本身返回之前向调用 PowerShell 代码发出条件信号。

Console.WriteLine()不能这样做,因为它直接写入控制台,完全绕过 PowerShell 的输出流。

最好的方法是将您的 .NET 类型实现为 PowerShell cmdlet,这将允许您使用其.Write*()方法立即写入 PowerShell 的输出流。

如果这不是一个选项,您可以使用 PowerShell SDK 写入调用运行空间的输出流,这有其局限性,如本答案中所述。

以下是适用于您的用例的技术演示:

# Start a background job.
$jb = Start-Job { 

  # Compile a sample type from C# source code.
  Add-Type -TypeDefinition @'
  using System.Management.Automation;

  public class SomeClass
  {

      public static string DoStuff()
      {
        // ... perform initialization

        // Instantly signal to the calling PowerShell session that initialization has
        // completed, by calling Write-Information, which writes to the information stream (6).
        using (PowerShell ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) {
          // IMPORTANT: Use .AddScript(), not .AddCommand().
          //            Even though .AddCommand() + .AddParameter() is arguably a cleaner way to
          //            formulate the command, it results in output that cannot be captured.
          // Even though Write-Information is silent by default, the information *is* written to the stream.
          ps.AddScript("Write-Information 'Initialized'").Invoke();
        }

        // Do other things.
        System.Threading.Thread.Sleep(2000);

        return "Done";

      }

  }
'@

  # Call the sample type's method, which runs for a while, and partway
  # through signals completion of the initialization portion with Write-Host output.
  [SomeClass]::DoStuff()

 }

 # Wait for output from the job and output it as it arrives. 
 while ($jb.HasMoreData) {

  # Receive pending stream output from the background job, capturing information-stream output separately.
  Receive-Job $jb -InformationVariable iv

  # Check if the initialization notification was received via the information stream.
  # Note that $iv is always a *collection* type, even if there's only 1 message,
  # and that the elements of that collection are of type [System.Management.Automation.InformationRecord]
  if ($iv -and $iv[0].ToString() -eq 'Initialized') { 'Now initialized.'}

  Start-Sleep -Milliseconds 100

}

Remove-Job $jb # clean up.

推荐阅读