Может ли Powershell запускать команды параллельно?
У меня есть скрипт powershell для выполнения пакетной обработки множества изображений, и я хотел бы выполнить параллельную обработку. Похоже, в Powershell есть некоторые опции фоновой обработки, такие как start-job, wait-job и т. Д., Но единственный хороший ресурс, который я нашел для выполнения параллельной работы, - это написание текста сценария и его запуск ( PowerShell Multithreading)
В идеале я хотел бы что-то вроде параллельного foreach в.net 4.
Нечто подобное выглядит так:
foreach-parallel -threads 4 ($file in (Get-ChildItem $dir))
{
.. Do Work
}
Может быть, мне было бы лучше, если бы я просто опустился на C#...
10 ответов
Вы можете выполнять параллельные задания в Powershell 2, используя фоновые задания. Проверьте Start-Job и другие командлеты задания.
# 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
Ответ Стива Таунсенда верен в теории, но не на практике, как отметил @likwid. Мой пересмотренный код учитывает барьер контекста задания - по умолчанию ничто не пересекает этот барьер! Автоматический $_
Таким образом, переменная может использоваться в цикле, но не может использоваться непосредственно в блоке сценария, потому что она находится в отдельном контексте, созданном заданием.
Чтобы передать переменные из родительского контекста в дочерний, используйте -ArgumentList
параметр на Start-Job
отправить его и использовать param
внутри блока скрипта, чтобы получить его.
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 *
(Как правило, я хотел бы предоставить ссылку на документацию PowerShell в качестве подтверждающего доказательства, но, увы, мой поиск оказался бесплодным. Если вам случится узнать, где задокументировано разделение контекста, оставьте здесь комментарий, чтобы сообщить мне!)
В наши дни на этот вопрос так много ответов:
- вакансии (или threadjobs в PS 6/7 или модуле)
- старт-процесс
- рабочие процессы
- API-интерфейс powershell с другим пространством выполнения
- invoke-command с несколькими компьютерами, каждый из которых может быть localhost (должен быть администратором)
- несколько вкладок сеанса (пространства выполнения) в ISE или вкладок удаленного PowerShell ISE
- Powershell 7 имеет
foreach-object -parallel
как альтернатива #4
Вот рабочие процессы с буквально foreach -parallel:
workflow work {
foreach -parallel ($i in 1..3) {
sleep 5
"$i done"
}
}
work
3 done
1 done
2 done
Или рабочий процесс с параллельным блоком:
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
Вот пример api с 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
В Powershell 7 вы можете использовать ForEach-Object -Parallel
$Message = "Output:"
Get-ChildItem $dir | ForEach-Object -Parallel {
"$using:Message $_"
} -ThrottleLimit 4
http://gallery.technet.microsoft.com/scriptcenter/Invoke-Async-Allows-you-to-83b0c9f0
я создал invoke-async, который позволяет вам запускать несколько блоков скриптов / командлетов / функций одновременно. это отлично подходит для небольших заданий (сканирование подсети или запрос wmi к сотням машин), потому что накладные расходы на создание пространства выполнения и время запуска start-job довольно значительны. Это можно использовать так.
со скриптовым блоком,
$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
просто командлет / функция
$servers = Get-Content servers.txt
$rtn = Invoke-Async -Set $servers -SetParam computername -Params @{count=1} -Cmdlet Test-Connection -ThreadCount 50
Фоновые задания дороги в настройке и не подлежат повторному использованию. У PowerShell MVP У Ойсена Грехана есть хороший пример многопоточности PowerShell http://www.nivot.org/2009/01/22/CTP3TheRunspaceFactoryAndPowerShellAccelerators.aspx
(25.10.2010 сайт не работает):
Я использовал адаптированный скрипт Oisin для использования в подпрограмме загрузки данных здесь:
http://rsdd.codeplex.com/SourceControl/changeset/view/a6cd657ea2be
Для завершения предыдущих ответов вы также можете использовать Wait-Job
ждать завершения всех заданий:
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
Если вы используете последнюю кросс-платформенную оболочку PowerShell (которую, кстати, следует) https://github.com/powershell/powershell, вы можете добавить один&
для запуска параллельных скриптов. (Используйте;
запускать последовательно)
В моем случае мне нужно было запустить 2 сценария npm параллельно: npm run hotReload & npm run dev
Вы также можете настроить npm для использования powershell
для своих скриптов (по умолчанию он использует cmd
на окнах).
Запускаем из корневой папки проекта: npm config set script-shell pwsh --userconfig ./.npmrc
а затем используйте одну команду сценария npm: npm run start
"start":"npm run hotReload & npm run dev"
В PowerShell 7.0 Preview 3 появилось новое встроенное решение.Параллельная функция PowerShell ForEach-Object
Итак, вы можете сделать:
Get-ChildItem $dir | ForEach-Object -Parallel {
.. Do Work
$_ # this will be your file
}-ThrottleLimit 4
На это дан исчерпывающий ответ. Просто хочу опубликовать этот метод, который я создал на основе Powershell-Jobs в качестве ссылки.
Задания передаются в виде списка блоков-скриптов. Их можно параметризовать. Выходные данные заданий имеют цветовую кодировку и префикс с индексом задания (точно так же, как в процессе vs-build, поскольку он будет использоваться в сборке).Может использоваться для запуска нескольких серверов одновременно или выполнения шагов сборки в параллельно или около того..
function Start-Parallel {
param(
[ScriptBlock[]]
[Parameter(Position = 0)]
$ScriptBlock,
[Object[]]
[Alias("arguments")]
$parameters
)
$jobs = $ScriptBlock | ForEach-Object { Start-Job -ScriptBlock $_ -ArgumentList $parameters }
$colors = "Blue", "Red", "Cyan", "Green", "Magenta"
$colorCount = $colors.Length
try {
while (($jobs | Where-Object { $_.State -ieq "running" } | Measure-Object).Count -gt 0) {
$jobs | ForEach-Object { $i = 1 } {
$fgColor = $colors[($i - 1) % $colorCount]
$out = $_ | Receive-Job
$out = $out -split [System.Environment]::NewLine
$out | ForEach-Object {
Write-Host "$i> "-NoNewline -ForegroundColor $fgColor
Write-Host $_
}
$i++
}
}
} finally {
Write-Host "Stopping Parallel Jobs ..." -NoNewline
$jobs | Stop-Job
$jobs | Remove-Job -Force
Write-Host " done."
}
}
образец вывода: