web-dev-qa-db-fra.com

Powershell: Exécuter plusieurs tâches en parallèle et afficher les résultats en continu des tâches en arrière-plan

Aperçu

Vous souhaitez appeler un script Powershell qui prend en compte un argument, exécute chaque travail en arrière-plan et affiche le résultat détaillé.

Problème que je rencontre

Le script semble s’exécuter, mais je veux le vérifier avec certitude en transmettant les résultats des tâches en arrière-plan pendant leur exécution.

Code

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

Ce que je vois actuellement:

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

Ce que je veux voir:

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

Je veux être capable de voir la sortie ou de la stocker quelque part afin de pouvoir me référer pour m'assurer que le script a été exécuté et voir quels serveurs ont été redémarrés, etc. 

Conclusion:

Dans le passé, j’exécutais le script et m’avait mis à jour les serveurs un par un et j’avais donné le résultat que je souhaitais, mais lorsque j’ai commencé à en créer d’autres - cette tâche a pris trop de temps, raison pour laquelle j’essaie d’utiliser le fond emplois avec "Start-Job". 

Quelqu'un peut-il m'aider à comprendre cela, s'il vous plaît?

15
talbert.houle

Vous pouvez consulter le module SplitPipeline ..__, spécialement conçu pour de telles tâches. Le code de démonstration est:

# 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

Pour votre tâche, remplacez "processing server $_"; sleep 1 (simule un travail lent) par un appel à votre script et utilisez la variable $_ comme entrée, le serveur actuel.

Si chaque travail n'utilise pas beaucoup de processeur, augmentez le paramètre Count (la valeur par défaut est le nombre de processeurs) afin d'améliorer les performances.

6
Roman Kuzmin

Ce n’est pas une nouvelle question, mais j’ai le sentiment qu’il manque une réponse incluant Powershell utilisant des workflows et ses possibilités parallèles, à partir de la version 3 de powershell. C’est moins de code et peut-être plus compréhensible que de commencer et d’attendre des emplois, ce qui fonctionne bien aussi.

J'ai deux fichiers: TheScript.ps1 qui coordonne les serveurs et BackgroundJob.ps1 qui effectue une sorte de vérification. Ils doivent être dans le même répertoire.

Le Write-Output dans le fichier de travail en arrière-plan écrit dans le même flux que celui présenté au démarrage de 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 (par exemple):

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

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

Ainsi, lors du démarrage du TheScript.ps1, il écrira "Serveur de traitement" 3 fois, mais il ne faudra pas attendre 15 secondes, mais 5 car ils sont exécutés en parallèle.

[Host3.com]  Processing server Host3.com
[Host2.com]  Processing server Host2.com
[Host1.com]  Processing server Host1.com
Done with all servers.
4
Piazzolla

Dans votre boucle ForEach, vous souhaiterez récupérer la sortie générée par les tâches en cours d'exécution.

Exemple non testé

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

Maintenant, si vous faites cela, tous vos résultats seront mélangés. Vous voudrez peut-être mettre un tampon sur votre sortie commentée pour dire de quelle sortie provient.

Ou

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
}

Il affichera toutes les sorties dès qu'un travail est terminé. Sans être mélangé.

1
E.V.I.L.

Si vous souhaitez que plusieurs tâches soient en cours, vous voudrez probablement modifier la sortie pour conserver la sortie associée à chaque tâche directement sur la 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'   
1
mjolinor

Vous pouvez obtenir les résultats en procédant de la sorte après que tous les travaux aient été reçus:

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

.ChildJobs.output aura tout ce qui a été renvoyé dans chaque travail.

0
powerkor