16

概述

希望调用一个接受参数的 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

我希望能够看到输出或将其存储在某处,以便我可以回顾以确保脚本运行并查看哪些服务器重新启动等。

结论:

过去,我运行脚本,它一次更新一个服务器,并给了我想要的输出,但是当我开始做更多服务器时 - 这个任务花费了太长时间,这就是我尝试使用后台的原因带有“开始工作”的工作。

谁能帮我解决这个问题,好吗?

4

5 回答 5

7

您可以查看模块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(默认为处理器计数)以提高性能。

于 2013-03-23T09:32:02.417 回答
6

这不是一个新问题,但我觉得它缺少一个答案,包括使用工作流的 Powershell 及其并行可能性,来自 powershell 版本 3。与开始和等待作业相比,它的代码更少,可能更容易理解,这当然也很好用。

我有两个文件:TheScript.ps1协调服务器和BackgroundJob.ps1进行某种检查。它们需要位于同一目录中。

Write-Output后台作业文件写入您在启动时看到的同一流TheScript.ps1

脚本.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.
于 2016-09-30T09:21:42.303 回答
1

在您的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
}

作业完成后,它将显示所有输出。不至于混为一谈。

于 2013-03-22T21:50:00.927 回答
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'   
于 2013-03-23T16:10:08.987 回答
0

在收到所有作业后,您可以通过执行以下操作来获得结果:

$array=@() Get-Job -Name * | 其中{$array+=$_.ChildJobs.output}

.ChildJobs.output 将包含每个作业中返回的任何内容。

于 2016-11-12T02:43:02.873 回答