12

PanoramicaPowerShell: Eseguire più posti di lavoro in parralel e vista in streaming i risultati di processi in background

Cercando di chiamare uno script PowerShell che prende in un argomento, viene eseguito ogni lavoro in background, e mi mostra l'output dettagliato.

problema che sto funzionando in

Lo script sembra funzionare, ma voglio verificare questo di sicuro in streaming i risultati dei processi in background mentre sono in esecuzione.

Codice

###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 

Quello che attualmente sto vedendo:

Id    Name   State  HasMoreData  Location    Command     
--    ----   -----  -----------  --------    -------     
23    Job23   Running True   localhost   #patch server ...   
25    Job25   Running True   localhost   #patch server ...   

Quello che voglio vedere:

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 

Voglio essere in grado di vedere l'output o negozio che da qualche parte in modo da poter rimando per essere sicuri che lo script ha funzionato e vedere quali server riavviati, ecc

Conclusione:

In passato, ho ha eseguito lo script ed è passato attraverso l'aggiornamento dei server uno alla volta e mi ha dato l'output che volevo, ma quando ho iniziato a fare più server - questa attività ha richiesto troppo tempo, motivo per cui sto cercando di utilizzare i processi in background con "Start- Lavoro".

Qualcuno può aiutarmi a capirlo, per favore?

risposta

4

Si può dare un'occhiata al modulo SplitPipeline. E 'progettato specificamente per tali compiti. Il codice demo di lavoro è:

# 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 

Per il vostro compito sostituire "processing server $_"; sleep 1 (simula un lavoro lento) con una chiamata allo script e utilizzare la variabile $_ come input, il server corrente.

Se ciascun lavoro non richiede un uso intensivo del processore, aumentare il parametro Count (il valore predefinito è il conteggio del processore) per migliorare le prestazioni.

+0

Roman, grazie per la risposta. Questo è il più vicino che potrei ottenere per avere l'output esattamente come lo volevo. Ottengo alcuni errori di output aggiuntivi, che risolverò, ma questo metodo non solo esegue il mio script, ma mi mostra l'output che desidero. Grazie. –

+0

@ talbert.houle, sono felice che tu abbia trovato utile questo strumento. Se hai idee su come renderlo migliore, puoi inviarlo sul sito del progetto. –

1

Nel ciclo ForEach è necessario acquisire l'output generato dai processi già in esecuzione.

Esempio Non Testato

$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 
} 

Ora, se si esegue questa operazione tutti i risultati saranno essere miscelati. Potresti voler mettere un timbro sul tuo output dettagliato per capire da quale output proviene.

O

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 
} 

Mostrerà tutto l'output non appena un lavoro è finito. Senza essere confusi.

+0

Grazie per la vostra informazione. Sfortunatamente, ho provato il primo blocco di codice e tutto ciò che ho ricevuto sullo schermo era "Starting job on servername". Ho aspettato più di 20 minuti per garantire che lo script venga eseguito sul server remoto, e questo è tutto ciò che sto ricevendo. Proverà il secondo blocco di codice e vedrà se qualcosa cambia. –

+0

[Ecco un link] (http://www.powershellmagazine.com/2013/02/13/pstip-streaming-results-from-background-jobs-in-powershell-3-0/) a qualcosa che potrebbe essere più utile. –

1

Se si desidera eseguire più processi in corso, è probabile che si desideri massaggiare l'output per aiutare a mantenere l'output con il lavoro direttamente sulla console.

$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' 
+0

Quando passo questo codice, lo script viene eseguito direttamente sopra l'istruzione "While" e non ricevo alcun output sulla console. –

+0

L'unica cosa che so che sarebbe che per $ server sia vuoto/nullo. Ho provato a usare questo scritpblock per l'azione del lavoro: {foreach ($ i in 1..10) {get-date; sleep -Seconds 2}} per creare un lavoro che viene eseguito per 20 secondi e produce output ogni 2 secondi, e $ servers = (1..3) come elenco dei server. – mjolinor

3

Non una nuova domanda, ma sento che manca una risposta compresa PowerShell utilizzando i flussi di lavoro e le sue possibilità parallele, da PowerShell versione 3. Qual è meno codice e forse più comprensibile di partenza e in attesa di posti di lavoro, che di ovviamente funziona bene.

Ho due file: TheScript.ps1 che coordina i server e BackgroundJob.ps1 che fa qualche tipo di controllo. Devono essere nella stessa directory.

Lo Write-Output nel file di lavoro in background scrive nello stesso flusso che viene visualizzato all'avvio di 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 (per esempio):

param (
    [Parameter(Mandatory=$true)] [string] $server 
) 

Write-Host "[$server]`t Processing server $server" 
Start-Sleep -Seconds 5 

Così quando si avvia il TheScript.ps1 si scriverà "server di elaborazione" 3 volte, ma non aspetterà 15 secondi ma invece 5 perché sono eseguiti in parallelo.

[host3.com] Processing server host3.com 
[host2.com] Processing server host2.com 
[host1.com] Processing server host1.com 
Done with all servers. 
+0

Mi piace che questa risposta non abbia bisogno di una libreria di terze parti! –

0

È possibile ottenere i risultati per fare qualcosa di simile dopo che tutti i posti di lavoro sono stati ricevuti:

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

.ChildJobs.output restituisce tutto ciò che è stato restituito in ogni lavoro.