Powershell运行空间输出的行为会有所不同,具体取决于定义返回的自定义对象的方式

时间:2018-07-28 01:52:46

标签: multithreading powershell runspace

我正在试验Powershell运行空间,并注意到根据我在哪里创建自定义对象,如何将输出写入控制台。如果我直接在脚本块中创建自定义对象,则输出将以表格式写入控制台。但是,当运行空间池仍具有开放线程时,该表似乎保持打开状态,即它创建了一个表,但我可以看到将完成的作业的结果动态附加到该表上。这是所需的行为。我将其称为行为1。

当我将一个自定义模块添加到运行空间池,然后调用该模块中包含的函数,然后创建一个自定义对象时,就会出现差异。对于每个返回的对象,此对象以列表格式打印到屏幕上。这不是所需的行为。我将这种行为称为2

我尝试将行为2的输出传递到Format-Table,但这只是为每个返回的对象创建一个新表。我可以通过使用Write-Host来打印一行对象值来达到预期的效果,但是考虑到似乎有一种内置的行为可以理解我想要的结果,因此我认为这样做不合适。

我对此事的想法是,它与运行空间的异步行为有关。我是PowerShell的新手,但是也许当自定义对象直接来自脚本块时,有一个隐藏的方法或类型声明告诉Powershell保持表打开并等待结果?使用第二种技术时,它将被覆盖,因为它来自我的自定义函数?

我想了解为什么会发生这种情况,以及如何在能够使用自定义模块的同时实现行为1,这最终将非常大。我也对另一种方法技术持开放态度,只要随着工作的完成它基本上可以看到输出表的增长。使用的代码如下。

$ISS = [InitialSessionState]::CreateDefault()
[void]$ISS.ImportPSModule(".\Modules\Test-Item.psm1")
$Pool = [RunspaceFactory]::CreateRunspacePool(1, 5, $ISS, $Host)
$Pool.Open()
$Runspaces = @()

# Script block to run code in
$ScriptBlock = {
    Param ( [string]$Server, [int]$Count )
    Test-Server -Server $Server -Count $Count

    # Uncomment the three lines below and comment out the two
    # lines above to test behavior 1.
    #[int] $SleepTime = Get-Random -Maximum 4 -Minimum 1
    #Start-Sleep -Seconds $SleepTime
    #[pscustomobject]@{Server=$Server; Count=$Count;}
}


# Create runspaces and assign to runspace pool
1..10 | ForEach-Object {
    $ParamList = @{ Server = "Server A" Count = $_ }
    $Runspace = [PowerShell]::Create()
    [void]$Runspace.AddScript($ScriptBlock)
    [void]$Runspace.AddParameters($ParamList)
    $Runspace.RunspacePool = $Pool
    $Runspaces += [PSCustomObject]@{
        Id = $_
        Pipe = $Runspace
        Handle = $Runspace.BeginInvoke()
        Object = $Object
    }
}

# Check for things to be finished
while ($Runspaces.Handle -ne $null)
{
    $Completed = $Runspaces | Where-Object { $_.Handle.IsCompleted -eq $true }

    foreach ($Runspace in $Completed)
    {
        $Runspace.Pipe.EndInvoke($Runspace.Handle)
        $Runspace.Handle = $null
    }

    Start-Sleep -Milliseconds 100
}

$Pool.Close()
$Pool.Dispose()

我正在使用的自定义模块如下。

function Test-Server {
    Param ([string]$Server, [int]$Count )
    [int] $SleepTime = Get-Random -Maximum 4 -Minimum 1
    Start-Sleep -Seconds $SleepTime

    [pscustomobject]@{Server = $Server;Item = $Count}
}

2 个答案:

答案 0 :(得分:0)

您所说的对我来说听起来很正常。之所以这样设计Powershell,是因为它承担了显示的负担。如果用户未指定显示方式,则PowerShell决定如何显示。

我无法使用提供的代码重现您的问题,但我认为这可以解决您的问题。

$FinalTable = foreach ($Runspace in $Completed)
{
    $Runspace.Pipe.EndInvoke($Runspace.Handle)
    $Runspace.Handle = $null
}

$FinalResult现在将具有您期望的表格格式。

答案 1 :(得分:0)

除了代码中的错误外,我的主要问题似乎是缺乏对Powershell默认对象处理的了解。当键/值对少于四个时,Powershell将对象的输出显示为表,如果键/值对少于四个,则将其显示为列表。

在我的测试模块中返回的自定义对象具有多个键值对,而直接返回的自定义对象只有两个。这导致我认为是奇怪的行为。我通过在发布的代码中删除一些键-值对来缩短它,然后又没有对其进行测试(对不起),使问题更加复杂。

This stackoverflow post有一个冗长的答案,解释了某些行为,并提供了更改默认输出的示例。