概述
希望调用一个 Powershell 脚本,该脚本接受一个参数,在后台运行每个作业,并向我显示详细的输出。
我遇到的问题
脚本似乎正在运行,但我想通过在后台作业运行时流式传输它们的结果来确定这一点。
代码
###StartServerUpdates.ps1 Script###
#get list of servers to update from text file and store in array
$servers=get-content c:\serverstoupdate.txt
#run all jobs, using multi-threading, in background
ForEach($server in $servers){
Start-Job -FilePath c:\cefcu_it\psscripts\PSPatch.ps1 -ArgumentList $server
}
#Wait for all jobs
Get-Job | Wait-Job
#Get all job results
Get-Job | Receive-Job
我目前看到的:
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
23 Job23 Running True localhost #patch server ...
25 Job25 Running True localhost #patch server ...
我想看的:
Searching for approved updates ...
Update Found: Security Update for Windows Server 2003 (KB2807986)
Update Found: Windows Malicious Software Removal Tool - March 2013 (KB890830)
Download complete. Installing updates ...
The system must be rebooted to complete installation.
cscript exited on "myServer" with error code 3.
Reboot required...
Waiting for server to reboot (35)
Searching for approved updates ...
There are no updates to install.
cscript exited on "myServer" with error code 2.
Servername "myServer" is fully patched after 2 loops
我希望能够查看输出或将其存储在某处,以便我可以回头参考以确保脚本运行并查看哪些服务器重新启动等。
结论:
过去,我运行脚本,它一次更新一个服务器,并给我想要的输出,但是当我开始做更多服务器时 - 这项任务花了太长时间,这就是我尝试使用的原因使用“Start-Job”进行后台作业。
谁能帮我解决这个问题吗?
您可以查看模块SplitPipeline。 它是专门为此类任务而设计的。工作演示代码是:
# import the module (not necessary in PS V3)
Import-Module SplitPipeline
# some servers (from 1 to 10 for the test)
$servers = 1..10
# process servers by parallel pipelines and output results immediately
$servers | Split-Pipeline {process{"processing server $_"; sleep 1}} -Load 1, 1
对于您的任务,请将
"processing server $_"; sleep 1
(模拟缓慢的作业)替换为对脚本的调用,并使用变量 $_
作为输入(当前服务器)。
如果每个作业不是处理器密集型,则增加参数
Count
(默认为处理器计数)以提高性能。
在 PowerShell 3 和 PowerShell 5.1 之间,PowerShell 具有具有并行可能性的“工作流”功能,代码更少,并且可能比启动和等待作业更容易理解,这当然也很好。
我有两个文件:
TheScript.ps1
用于协调服务器,BackgroundJob.ps1
用于进行某种检查。它们需要位于同一目录中。
后台作业文件中的
Write-Output
写入您在启动 TheScript.ps1
时看到的同一流。
TheScript.ps1:
workflow parallelCheckServer {
param ($Servers)
foreach -parallel($Server in $Servers)
{
Invoke-Expression -Command ".\BackgroundJob.ps1 -Server $Server"
}
}
parallelCheckServer -Servers @("host1.com", "host2.com", "host3.com")
Write-Output "Done with all servers."
BackgroundJob.ps1(例如):
param (
[Parameter(Mandatory=$true)] [string] $server
)
Write-Host "[$server]`t Processing server $server"
Start-Sleep -Seconds 5
因此,当启动
TheScript.ps1
时,它会写入“处理服务器”3次,但它不会等待15秒,而是等待5秒,因为它们是并行运行的。
[host3.com] Processing server host3.com
[host2.com] Processing server host2.com
[host1.com] Processing server host1.com
Done with all servers.
在
ForEach
循环中,您需要获取已运行的作业生成的输出。
未测试示例
$sb = {
"Starting Job on $($args[0])"
#Do something
"$($args[0]) => Do something completed successfully"
"$($args[0]) => Now for something completely different"
"Ending Job on $($args[0])"
}
Foreach($computer in $computers){
Start-Job -ScriptBlock $sb -Args $computer | Out-Null
Get-Job | Receive-Job
}
现在如果你这样做,你的所有结果都会好坏参半。您可能想在详细输出上打上标记来区分哪个输出来自。
或者
Foreach($computer in $computers){
Start-Job -ScriptBlock $sb -Args $computer | Out-Null
Get-Job | ? {$_.State -eq 'Complete' -and $_.HasMoreData} | % {Receive-Job $_}
}
while((Get-Job -State Running).count){
Get-Job | ? {$_.State -eq 'Complete' -and $_.HasMoreData} | % {Receive-Job $_}
start-sleep -seconds 1
}
作业完成后,它将显示所有输出。不混淆。
如果您想要进行多个作业,您可能需要调整输出,以帮助直接在控制台上保持哪个输出与哪个作业一致。
$BGList = 'Black','Green','DarkBlue','DarkCyan','Red','DarkGreen'
$JobHash = @{};$ColorHash = @{};$i=0
ForEach($server in $servers)
{
Start-Job -FilePath c:\cefcu_it\psscripts\PSPatch.ps1 -ArgumentList $server |
foreach {
$ColorHash[$_.ID] = $BGList[$i++]
$JobHash[$_.ID] = $Server
}
}
While ((Get-Job).State -match 'Running')
{
foreach ($Job in Get-Job | where {$_.HasMoreData})
{
[System.Console]::BackgroundColor = $ColorHash[$Job.ID]
Write-Host $JobHash[$Job.ID] -ForegroundColor Black -BackgroundColor White
Receive-Job $Job
}
Start-Sleep -Seconds 5
}
[System.Console]::BackgroundColor = 'Black'
收到所有作业后,您可以通过执行以下操作来获取结果:
$数组=@() 获取工作-姓名 * |其中{$array+=$_.ChildJobs.output}
.ChildJobs.output 将包含每个作业中返回的任何内容。