Powershell 运行空间输出的行为因返回自定义对象的定义方式而异
Powershell runspace output behaves differently depending on how returning custom object is defined
我正在尝试使用 Powershell 运行空间,并注意到根据我创建自定义对象的位置,输出写入控制台的方式有所不同。如果我直接在我的脚本块中创建自定义对象,输出将以 table 格式写入控制台。然而,table 似乎保持打开状态,而运行空间池仍然有打开的线程,即它创建了一个 table 但我可以看到完成的作业的结果被动态附加到 table .这是期望的行为。我将此称为行为 1。
当我将自定义模块添加到运行空间池,然后调用该模块中包含的函数,然后创建自定义对象时,就会出现差异。对于每个返回的对象,此对象以列表格式打印到屏幕。这不是期望的行为。我将此行为称为 2
我尝试将行为 2 的输出通过管道传输到 Format-Table,但这只会为每个返回的对象创建一个新的 table。我可以通过使用 Write-Host 打印一行对象值来达到预期的效果,但我认为这是不合适的,因为似乎有一种内置的行为可以实现我想要的结果,如果我能理解的话。
我对此事的看法是它与运行空间的异步行为有关。我是 powershell 的新手,但也许当自定义对象直接来自脚本块时,有一个隐藏的方法或类型声明告诉 powershell 保持 table 打开并等待结果?这在使用第二种技术时会被覆盖,因为它来自我的自定义函数?
我想了解为什么会发生这种情况,以及如何在能够使用最终会非常大的自定义模块的同时实现行为 1。我也对不同的方法技术持开放态度,只要它可以从本质上看到 table 的输出随着作业的完成而增长。使用的代码如下。
$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}
}
我觉得你提到的完全正常。 powershell 就是这样设计的,因为它分担了显示的负担。如果用户没有指定如何显示,PowerShell 决定如何显示。
我无法使用提供的代码重现您的问题,但我认为这会解决您的问题。
$FinalTable = foreach ($Runspace in $Completed)
{
$Runspace.Pipe.EndInvoke($Runspace.Handle)
$Runspace.Handle = $null
}
$FinalResult
现在将具有您期望的 table 格式。
看来,除了我的代码中的错误之外,我的主要问题是缺乏对 powershell 的默认对象处理的理解。当少于四个键值对时,Powershell 将对象的输出显示为 table,当键值对更多时,显示为列表。
我的测试模块中返回的自定义对象有多个键值对,而我直接返回的自定义对象只有两个。这导致了我认为奇怪的行为。我通过删除我发布的代码中的一些键值对来缩短它然后没有测试它(抱歉)使问题更加复杂。
有一个冗长的答案,解释了一些行为并提供了更改默认输出的示例。
我正在尝试使用 Powershell 运行空间,并注意到根据我创建自定义对象的位置,输出写入控制台的方式有所不同。如果我直接在我的脚本块中创建自定义对象,输出将以 table 格式写入控制台。然而,table 似乎保持打开状态,而运行空间池仍然有打开的线程,即它创建了一个 table 但我可以看到完成的作业的结果被动态附加到 table .这是期望的行为。我将此称为行为 1。
当我将自定义模块添加到运行空间池,然后调用该模块中包含的函数,然后创建自定义对象时,就会出现差异。对于每个返回的对象,此对象以列表格式打印到屏幕。这不是期望的行为。我将此行为称为 2
我尝试将行为 2 的输出通过管道传输到 Format-Table,但这只会为每个返回的对象创建一个新的 table。我可以通过使用 Write-Host 打印一行对象值来达到预期的效果,但我认为这是不合适的,因为似乎有一种内置的行为可以实现我想要的结果,如果我能理解的话。
我对此事的看法是它与运行空间的异步行为有关。我是 powershell 的新手,但也许当自定义对象直接来自脚本块时,有一个隐藏的方法或类型声明告诉 powershell 保持 table 打开并等待结果?这在使用第二种技术时会被覆盖,因为它来自我的自定义函数?
我想了解为什么会发生这种情况,以及如何在能够使用最终会非常大的自定义模块的同时实现行为 1。我也对不同的方法技术持开放态度,只要它可以从本质上看到 table 的输出随着作业的完成而增长。使用的代码如下。
$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}
}
我觉得你提到的完全正常。 powershell 就是这样设计的,因为它分担了显示的负担。如果用户没有指定如何显示,PowerShell 决定如何显示。
我无法使用提供的代码重现您的问题,但我认为这会解决您的问题。
$FinalTable = foreach ($Runspace in $Completed)
{
$Runspace.Pipe.EndInvoke($Runspace.Handle)
$Runspace.Handle = $null
}
$FinalResult
现在将具有您期望的 table 格式。
看来,除了我的代码中的错误之外,我的主要问题是缺乏对 powershell 的默认对象处理的理解。当少于四个键值对时,Powershell 将对象的输出显示为 table,当键值对更多时,显示为列表。
我的测试模块中返回的自定义对象有多个键值对,而我直接返回的自定义对象只有两个。这导致了我认为奇怪的行为。我通过删除我发布的代码中的一些键值对来缩短它然后没有测试它(抱歉)使问题更加复杂。