web-dev-qa-db-fra.com

Powershell peut-il exécuter des commandes en parallèle?

J'ai un script PowerShell permettant d'effectuer un traitement par lots sur un tas d'images et j'aimerais effectuer un traitement en parallèle. Powershell semble avoir certaines options de traitement en arrière-plan telles que start-job, wait-job, etc., mais la seule bonne ressource que j'ai trouvée pour travailler en parallèle consistait à écrire le texte d'un script et à l'exécuter ( Multithreading PowerShell )

Idéalement, je voudrais quelque chose qui ressemble à foreach parallèle dans .net 4.

Quelque chose d'assez joli comme:

foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
   .. Do Work
}

Peut-être que je ferais mieux de descendre à c # ...

108
Alan Jackson

Vous pouvez exécuter des tâches parallèles dans Powershell 2 à l’aide de tâches en arrière-plan . Check out Start-Job et les autres applets de commande de travail.

# Loop through the server list
Get-Content "ServerList.txt" | %{

  # Define what each job does
  $ScriptBlock = {
    param($pipelinePassIn) 
    Test-Path "\\$pipelinePassIn\c`$\Something"
    Start-Sleep 60
  }

  # Execute the jobs in parallel
  Start-Job $ScriptBlock -ArgumentList $_
}

Get-Job

# Wait for it all to complete
While (Get-Job -State "Running")
{
  Start-Sleep 10
}

# Getting the information back from the jobs
Get-Job | Receive-Job
85
Steve Townsend

La réponse de Steve Townsend est correcte en théorie mais pas en pratique, comme l'a souligné @likwid. Mon code révisé prend en compte la barrière travail-contexte - rien ne dépasse cette barrière par défaut! La variable automatique $_ peut donc être utilisée dans la boucle mais ne peut pas être utilisée directement dans le bloc de script car elle se trouve dans un contexte séparé créé par le travail.

Pour transmettre des variables du contexte parent au contexte enfant, utilisez le paramètre -ArgumentList sur Start-Job pour l'envoyer et utilisez param à l'intérieur du bloc de script pour le recevoir.

cls
# Send in two root directory names, one that exists and one that does not.
# Should then get a "True" and a "False" result out the end.
"temp", "foo" | %{

  $ScriptBlock = {
    # accept the loop variable across the job-context barrier
    param($name) 
    # Show the loop variable has made it through!
    Write-Host "[processing '$name' inside the job]"
    # Execute a command
    Test-Path "\$name"
    # Just wait for a bit...
    Start-Sleep 5
  }

  # Show the loop variable here is correct
  Write-Host "processing $_..."

  # pass the loop variable across the job-context barrier
  Start-Job $ScriptBlock -ArgumentList $_
}

# Wait for all to complete
While (Get-Job -State "Running") { Start-Sleep 2 }

# Display output from all jobs
Get-Job | Receive-Job

# Cleanup
Remove-Job *

(J'aime généralement fournir une référence à la documentation PowerShell en tant que preuve à l'appui, mais, hélas, ma recherche a été infructueuse. Si vous savez où la séparation de contexte est documentée, postez un commentaire ici pour me le faire savoir!)

89
Michael Sorens

http://gallery.technet.Microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f

j'ai créé un invoke-async qui vous permet d'exécuter plusieurs blocs de script/applets de commande/fonctions en même temps. c'est très bien pour les petits travaux (analyse de sous-réseau ou requête wmi sur des centaines de machines) car la surcharge liée à la création d'un espace d'exécution par rapport au temps de démarrage du travail de démarrage est assez drastique. Il peut être utilisé comme tel.

avec scriptblock,

$sb = [scriptblock] {param($system) gwmi win32_operatingsystem -ComputerName $system | select csname,caption} 

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $server -SetParam system  -ScriptBlock $sb

juste applet de commande/fonction

$servers = Get-Content servers.txt 

$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50
8
jrich523

Les tâches d’arrière-plan sont coûteuses à installer et ne sont pas réutilisables. Le MVP PowerShell Oisin Grehan a n bon exemple du multi-threading PowerShell.

(Le site est en panne, mais accessible via les archives Web).

J'ai utilisé le script Oisin adapté pour une utilisation dans une routine de chargement de données ici:

http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be#Invoke-RSDDThreaded.ps1

7
Chad Miller

Pour compléter les réponses précédentes, vous pouvez également utiliser Wait-Job pour attendre que toutes les tâches soient terminées:

For ($i=1; $i -le 3; $i++) {
    $ScriptBlock = {
        Param (
            [string] [Parameter(Mandatory=$true)] $increment
        )

        Write-Host $increment
    }

    Start-Job $ScriptBlock -ArgumentList $i
}

Get-Job | Wait-Job | Receive-Job
3
Thomas

Il y a tellement de réponses à cela ces jours-ci:

  1. travaux (ou tâches dans PS 6 ou le module)
  2. processus de démarrage
  3. flux de travail
  4. powershell api avec un autre runspace
  5. invoke-command avec plusieurs ordinateurs, qui peuvent tous être locauxhost (doivent être admin)
  6. plusieurs onglets de session dans l'ise

Voici des workflows avec littéralement un foreach -parallel:

workflow work {
  foreach -parallel ($i in 1..3) { 
    sleep 5 
    "$i done" 
  }
}

work

3 done
1 done
2 done

Ou un workflow avec un bloc parallèle:

function sleepfor($time) { sleep $time; "sleepfor $time done"}

workflow work {
  parallel {
    sleepfor 3
    sleepfor 2
    sleepfor 1
  }
  'hi'
}

work 

sleepfor 1 done
sleepfor 2 done
sleepfor 3 done
hi

Voici un exemple d'api avec runspaces:

$a =  [PowerShell]::Create().AddScript{sleep 5;'a done'}
$b =  [PowerShell]::Create().AddScript{sleep 5;'b done'}
$c =  [PowerShell]::Create().AddScript{sleep 5;'c done'}
$r1,$r2,$r3 = ($a,$b,$c).begininvoke() # run in background
$a.EndInvoke($r1); $b.EndInvoke($r2); $c.EndInvoke($r3) # wait
($a,$b,$c).streams.error # check for errors
($a,$b,$c).dispose() # clean

a done
b done
c done
1
js2010