Вспоминаем Powershell в нескольких частях. Часть 4. Параллельное исполнение: Powershell jobs
Для ЛЛ: серия пометок по костылям
Конечная цель серии постов: написать свой очень маленький и очень кривой WSUS, поскольку развития WSUS больше не предвидится, но я про это писал
Часть 1. База из баз. Теория
Часть 1.1 Зачем ставить обновления на Linux и Windows и куда угодно, если в отделе работают проверенные электроником сотрудники, и все работает?
Часть 1.2 Чем плох WSUS, SCOM, прочее ПО, и факты в Ansible?
Часть 1.3 Почему Powershell, а не Python?
Часть 1.4 Прочие базовые вещи
Часть 1.5 Классы и объекты, для тех, кто пропускал школу
Часть 1.6 К теме обновлений в Windows
Часть 1.7 Как это все хранить и обрабатывать?
Часть 1.8 Давайте начинать. Мой первый класс
Часть 1.9 Немного магии, не очевидной с первого раза
Часть 1.10 Мой первый массив
Часть 1.11 Суй массив в файл. И забирай из файла
Часть 1.12 Теперь все вместе
Часть 2. Windows update
Часть 2.1 Служба обновлений и ее журнал
Часть 2.2 Настраиваем удаленный доступ
Часть 2.3 Ловим исключения
Часть 2.4 Проблема слишком больших прав
Часть 2.5 Разрешение удаленного подключения
Часть 2.6 Параллельная обработка задач, -parallel, powershell jobs, Runspaces
Часть 2.7 И, наконец, получим первый список
Часть 2.8 Итого
Часть 3. Обрабатываем, что получилось
Часть 3.1 Немного про общую логику
Часть 3.2 В предыдущих сериях
Часть 3.3 Обновление списка обновлений
Часть 3.4 Обрабатываем оба списка сразу – список обновлений и список с сервера
Часть 3.5 Осталось только выгрузить в Excel
Часть 3.6 Отладка и наладка
Часть 3.7 Альтернативы?
Часть 4. Параллельное исполнение: Powershell jobs
Часть 4.1 Почему Powershell jobs
Часть 4.2 Руководство
Часть 4.3 Переходим к примерам Powershell jobs
Часть 4.4 Переходим к практике Powershell jobs
Часть 4. Параллельное исполнение: Powershell jobs
Часть 4.1 Почему Powershell jobs
У Powershell есть несколько вариантов параллельного исполнения кода.
Первый и наиболее известный, это Foreach-Parallel
Второй - Start-Job и Start-ThreadJob
В чем минус Job ? Это параллельное исполнение скрипта, из результатов которого еще и надо задачу получить. Плюс, точнее минус, этому процессу сложно отдавать какой-то объект для исполнения, у него непривычная схема того, что ему можно отдать на вход. То, что в руководстве описано как «Example 10: Use the ArgumentList parameter to specify an array»
В чем плюс? Сохраняется читаемость, в моем случае. Я ж не настоящий программист.
Часть 4.2 Руководство
В руководстве написано крайне .. плохо -
Start-Job -ScriptBlock
и дальше пишите себе что хотите.
но ScriptBlock это крайне, крайне неудобно, если не читать описание:
$a = { Get-Service BITS }
Invoke-Command -ScriptBlock $a
То есть, скриптблок можно описать почти как функцию
Like functions, script blocks can include the dynamicparam, begin, process, and end keywords
только есть нюансы.
Часть 4.3 Переходим к примерам Powershell jobs
Пример
$PfadZurSpeicherdatenbank = $PSScriptRoot
$Skriptversion = "12"
$Block1 =
{Get-ChildItem}Start-Job -ScriptBlock $Block1 -Name JobExample01
Start-Job -ScriptBlock $Block1 -Name JobExample01 # это повтор строки и так надо для примера
Get-Job
Get-Job -Name JobExample01
Receive-Job -Name JobExample01
Обратите внимание на параметры State и HasMoreData
Get-Job | Select Id, Name, PSJobTypeName, State, HasMoreData
Get-Job | Select Id, Name, PSJobTypeName, State, HasMoreData | format-table
Сделайте
$Data2 = Receive-Job -Name JobExample01
$Data2
Обратите внимание.
Я запросил итоги задачи «-Name JobExample01». Таких задач в списке должно быть две,
и в переменную попали оба вывода, от двух задач с разными ID и одинаковыми именами. ID уникален, имя – нет, зато имя можно генерировать.
Теперь сделайте все что выше в виде одного скрипта, и удивитесь – в вывод $Data2 не попало ничего. Хотя задачи в списке есть. Но вывод из задачи со статусом Completed и HasMoreData = False вы уже получили, а вывод из задачи, которая еще выполняется (Running), хотя данные из нее еще не получали, но HasMoreData =True.
Удалим все задачи.
Get-Job | Remove-Job
Чтобы перейти дальше, сделаем еще один пример из руководства:
$a ={ param($p1, $p2)
"p1: $p1"
"p2: $p2"}&$a -p2 "First" -p1 "Second"
И примеры не из руководства: пример 1
$Job02 = {
param ($Out500001)
write-host "JB1" $Out500001}Start-Job -ScriptBlock $Job02 -ArgumentList "ABC123"
Start-Sleep 5; Get-Job ;
Get-Job | Receive-Job
Get-Job | Remove-Job
И примеры не из руководства: пример 2
$Job03 = {
param ($Out500001)
foreach ($Job03data in $Out500001){
write-host "JB3" $Job03data}}$Job03Array = (1,2,22)
Start-Job -ScriptBlock $Job03 -ArgumentList $Job03Array
Start-Sleep 5;
Get-Job | Receive-Job
И примеры не из руководства: пример 3
$Job04 = {
param ($Out500001)
foreach ($Job03data in $Out500001){
write-host "JB4" $Job03data}}$Job03Array = (1,2,22)
Start-Job -ScriptBlock $Job04 -ArgumentList (,$Job03Array)
Start-Sleep 5;
Get-Job | Receive-Job
И примеры не из руководства: пример 4
$Job05 = {
param ($Input01, $Input02)
write-host "JB5-1 " $Input01
write-host "JB5-2 " $Input02}Start-Job -ScriptBlock $Job05 -ArgumentList ("A123", "B456")
Start-Sleep 5;
Get-Job | Receive-Job
Тут стоило описать, как использовать именованные переменные \ параметры, но я не стал. Вроде можно. Но мне не очень нужно.
Выводы из увиденного в примерах предлагается сделать самостоятельно
Часть 4.4 Переходим к практике Powershell jobs
И теперь делаем все сразу!
$PfadZurSpeicherdatenbank = $PSScriptRoot
$Skriptversion = "12"
$MeineErsteSicherungsdatei = $PfadZurSpeicherdatenbank + "\" + "nur-eine-datei-part12.xml"
$Fehlermeldung01 = "Achtung, da ist etwas schiefgelaufen"if (Test-Path $MeineErsteSicherungsdatei){$NewKeinArray = Import-Clixml -Path $MeineErsteSicherungsdatei}
else {Write-Host $Fehlermeldung01}$MeineErsteLogin = $PfadZurSpeicherdatenbank + "\" + "dieparole.txt"
$DasPasswordFileExist = Test-Path -Path $MeineErsteLogin
if ($DasPasswordFileExist -eq $false) {$DieParole = Get-Credential
Export-Clixml -Path $MeineErsteLogin -InputObject $DieParole}
else {$DieParole = Import-Clixml $MeineErsteLogin}$MeineServerliste = @("192.168.122.250","192.168.122.251")
# $DataRemote1 = Get-HotFix -ComputerName $MeineServerliste[0] -Credential $DieParole$Rn = Get-Random -Minimum 1 -Maximum 200
$NameRn = "JobExample12" + $Rn
$Block2 = {
param ($ServerAsParam, $CredentialsAsParam)
$DataInBlockRemote1 = Get-HotFix -ComputerName $ServerAsParam -Credential $CredentialsAsParam
Return $DataInBlockRemote1}Start-Job -ScriptBlock $Block2 -ArgumentList ($MeineServerliste[0], $DieParole) -Name $NameRn
Start-Sleep 5;
$DataInBlock = Get-Job -Name $NameRn| Receive-Job
Заключение
На этом как бы все. Все необходимые элементы для своего личного костыля, если он вам действительно нужен, у вас есть.
Литература
Как использовать циклы While и Foreach в Powershell Foreach на примерах
Running ForEach in parallel on Windows Powershell 5 (and older)
about_Foreach
about_Foreach-Parallel
Optimize performance using parallel execution
Работа с фоновыми процессами через Jobs в Powershell
Named Arguments for PowerShell Functions: Best Practices
PowerShell background jobs unlock scripting performance
about_Script_Blocks
PowerShell Tip –> Passing array as an argument to a Job/ ScriptBlock
Возврат значения из powershell invoke-command агенту SQL-Server