Глава 14. Многозадачность с фоновыми заданиями
Содержание
- Глава 14. Многозадачность с фоновыми заданиями
- Заставляем PowerShell выполнять множество вещей в одно время
- Сопоставление синхронного и асинхронного
- Создание задания процесса
- Создание задания потока
- Удалённая работа как задание
- Задания в природе
- Получение результатов задания
- Работа с дочерними заданиями
- Команды для управления заданиями
- Общие моменты путаницы
- Лабораторные занятия
- Ответы лабораторных занятий
Все всегда говорят вам про многозадачность, не так ли? Почему бы PowerShell не помочь вам в этом, выполняя одновременно более одного действия? Оказывается, именно это может делать PowerShell, в особенности для длительных задач, которые могут включать множество целевых компьютеров. Прежде чем погрузиться в эту главу, убедитесь что вы прочли Главу 13, потому как мы продвинем на шаг вперёд эти понятия удалённого взаимодействия.
Будьте внимательны! | |
---|---|
Мы будем применять большое число командлетов Az, что потребует активной подписки Azure. Это всего лишь примеры, которые мы выбрали для выделения. |
Вы должны представлять себе PowerShell как приложение с единственным потоком, что подразумевает, что он способен выполнять только одно дело за раз. Вы набираете команду, вы нажимаете Enter и ваша оболочка дожидается исполнения этой команды. Вы не сможете выполнить вторую команду пока не завершится первая.
Однако при своих функциональных возможностях фоновых заданий, PowerShell обладает возможностью помещать команду в отдельный фоновый поток или обосабливать фоновый процесс PowerShell. Это делает возможным такой команде исполняться в фоновом режиме, поскольку вы продолжаете применять свою оболочку для другой задачи. Вы должны принять такое решение перед исполнением соответствующей команды; после того как вы нажмёте Enter, вы не сможете принять решение о перемещении команд длительного исполнения в свой фоновый режим.
После того как команды находятся в фоновом режиме, PowerShell предоставляет механизмы для проверки их состояния, выборки всех результатов и тому подобного.
Для начала давайте разберёмся с терминологией. Обычные команды PowerShell исполняет синхронно, то есть вы нажимаете Enter, а затем дожидаетесь завершения команды. Перемещение задания в фоновый режим делает возможным его выполнение асинхронно, что означает что вы можете продолжать применять свою оболочку для прочих задач по мере завершения команды. Давайте взглянем на некоторые важные различия между исполнением команд такими двумя способами:
-
Когда вы выполняете команду синхронно, вы можете откликаться на запросы на ввод. Когда вы можете запускать команды в фоновом режиме, нет никакой возможности увидеть запросы на ввод - по- существу, они остановят исполнение такой команды.
-
Когда что- то идёт не так, синхронные команды вырабатывают сообщения об ошибке. Фоновые команды производят ошибки, однако вы не можете увидеть их немедленно. Для их перехвата, если это необходимо, вам придётся выполнить определённые мероприятия (Глава 24 поясняет как выполнять это.)
-
Когда вы пропускаете необходимый параметр в синхронной команде, PowerShell имеет возможность выдать вам приглашение на недостающие сведения. Для фоновой команды вы этого не сможете сделать, а потому команда откажет.
-
Получаемые результаты синхронной команды начнут отображаться как только они станут доступными. Для команды в фоновом режиме вы дожидаетесь завершения исполнения команды и затем выполняете выборку кэшированных результатов.
Обычно вы исполняете команды в синхронном режиме для их проверки и чтобы получить их работающими как положено, а затем исполняете их в фоновом режиме только после того, как они полностью отлажены и работают ожидаемым образом. Мы следуем таким мерам чтобы гарантировать, что такая команда будет работать без проблем и что у неё будет больше шансов осуществления в фоновом режиме. Команды в фоновом режиме PowerShell именует заданиями (jobs). Вы можете создавать задания несколькими способами, а для управления ими вы можете применять различные команды.
Дополнительно | |
---|---|
Технически говоря, те задания, которые мы обсуждаем в этой главе являются лишь несколькими видами заданий, с которыми вы можете сталкиваться. Задания являются неким моментом расширения для PowerShell, что означает, что для кого- то (либо в Microsoft, либо в качестве стороннего разработчика) имеется возможность создания иных вещей с названием заданий, которые выглядят и работают несколько иначе чем то, что мы описываем в данной главе. При расширении оболочки под различные цели вы можете сталкиваться с иными видами заданий. Мы хотим чтобы вы уяснили эту маленькую подробность и знали бы, что то, что вы изучаете в данной главе применимо лишь к естественным, обычным заданиям, которые поставляются с PowerShell. |
Самый первый рассматриваемый нами тип заданий, вероятно, самый простой: задание процесса. Это некая команда, которая запускается в ином процессе PowerShell в вашей машине в фоновом режиме.
Для запуска одного из таких заданий вы можете применять команду Start-Job
.
Параметр -ScriptBlock
позволяет вам определять саму команду (или команды) для исполнения.
PowerShell выставляет имена заданий по умолчанию (Job1
,
Job2
и т.д.), или же вы можете определять индивидуальное название задания при помощи
параметра -Name
. Вместо того чтобы определять блок сценария, вы можете задавать
свой параметр -FilePath
чтобы обладать заданием, исполняющим файл сценария, наполненный
командами. Вот простой пример:
PS /Users/travisp/> start-job -scriptblock { gci }
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Running True localhost gci
Данная команда создаёт свой объект задания и, как показывает наш предыдущий пример, это задание начинает исполняться немедленно. Такому заданию также выделяется последовательный номер идентификатора задания, который указан в этой таблице.
Данная команда также обладает параметром -WorkingDirectory
, который позволяет вам
изменять где в вашей файловой системе стартует ваше задание. По умолчанию, оно всегда стартует в вашем домашнем каталоге.
Никогда не применяйте никаких предположений о путях к файлам из задания в фоновом режиме: применяйте абсолютные пути, чтобы
гарантировать что вы можете ссылаться на любые файлы, которые могут потребоваться вашей команде задания, либо применяйте
параметр -WorkingDirectory
. Вот образец:
PS /Users/travisp/> start-job -scriptblock { gci } -WorkingDirectory /tmp
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
3 Job3 BackgroundJob Running True localhost gci
Внимательные читатели обратят внимание, что самое первое созданное нами задание носит название Job1
и приданный ему идентификатор 1
, Однако вторым заданием является
Job3
с идентификатором 3
. Оказывается, у каждого
задания имеется по крайней мере одно дочернее задание (child job), и самому первому
заданию (потомку Job1
) придаются название Job2
и
значение идентификатора 2
. Позднее в этой главе мы вернёмся к дочерним заданиям.
Здесь нужно иметь в виду: хотя задания процессов выполняются локально, они требуют разрешения удалённого взаимодействия PowerShell, которое мы рассматривали в Главе 13.
Существует второй тип задания, который поставляется как часть PowerShell и о котором мы бы хотели поговорить. Он носит название задания потока (thread job). Вместо того чтобы запускать совершенно другой процесс PowerShell, задание потока раскручивается в другом потоке в том же самом процессе. Вот образец:
PS /Users/travisp/> start-threadjob -scriptblock { gci }
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 ThreadJob Running False PowerShell gci
Ха, выглядит очень похожим на вывод нашего предыдущего задания? Всего лишь два отличия - значение
PSJobTypeName
которым выступает ThreadJob
и
значение Location
, а именно PowerShell
.
Это сообщает нам что данное задание запущено внутри самого процесса, который мы применяем в данный момент, однако в другом
потоке.
Поскольку накладные расходы раскрутки нового потока драматически быстрее нежели чем раскрутка нового процесса, задания потока фантастичны для краткосрочных сценариев и команд, которые вы бы желали запускать быстро и исполнять в фоновом режиме. И наоборот, вы можете пользоваться в своей машине заданиями процессов для сценариев с длительным временем исполнения.
Будьте внимательны! | |
---|---|
Хотя задания потоков стартуют быстрее, имейте в виду, что в одном процессе одновременно выполняться может лишь
определённое число потоков, прежде чем он начнёт замедляться. PowerShell изготовлен с "пределом дросселирования"
10, чтобы помочь вам в том, чтобы не слишком застревать в PowerShell. Это означает, что одновременно могут исполняться
только 10 заданий потоков. Если вы пожелаете увеличить этот предел, у вас есть такая возможность. Просто определите параметр
|
Давайте рассмотрим заключительную технологию, которую вы можете применять для создания нового задания: возможности
удалённого взаимодействия PowerShell, которые мы изучали в Главе 13.
Имеется существенное отличие: всякая команда, которую вы определяете в -scriptblock
(или в -command
, что является синонимом для того же самого параметра) будет передаваться
параллельно во все заданные вами компьютеры. За раз может быть осуществлён контакт с 32 компьютерами (если только вы не
изменили параметр -throttleLimit
для разрешения большего или меньшего числа), поэтому
когда вы определите более 32 названий компьютеров, запустятся только первые 32. Остальные будут запущены после того как первый
набор начнёт завершаться и задание верхнего уровня покажет состояние выполненного после завершения во всех компьютерах.
В отличии от прочих двух способов запуска задания, данная технология потребует чтобы вы имели установленной PowerShell v6 или выше в каждом из целевых компьютеров и включённым в PowerShell всех целевых компьютеров удалённым взаимодействием поверх SSH. Поскольку такая команда физически выполняется в каждом из удалённых компьютеров, вы распределяете свою вычислительную нагрузку, что может способствовать улучшению производительности для сложных команд с длительным временем выполнения. Получаемый результат возвращается обратно в ваш компьютер и сохраняется в своём задании пока вы не будете готовы просмотреть его.
В нашем следующем примере вы также видите параметр -JobName
, который позволяет вам
задавать название задания, отличающееся от порождаемого по умолчанию:
PS C:\> invoke-command -command { get-process } -hostname (get-content .\allservers.txt ) -asjob -jobname MyRemoteJob
WARNING: column "Command" does not fit into the display and was removed.
Id Name State HasMoreData Location
-- ---- ----- ----------- --------
8 MyRemoteJob Running True server-r2,lo...
Мы хотим воспользоваться данным разделом чтобы показать некий пример модуля PowerShell, который выставляет свои собственные
PSJobs, поэтому вы можете рассматривать его как шаблон в своём путешествии PowerShell. Давайте, например, возьмём команду
New-AzVm
:
PS /Users/travisp/> gcm New-AzVM -Syntax
New-AzVM -Name <string> -Credential <pscredential> [-ResourceGroupName
<string>] [-Location <string>] [-Zone <string[]>] [-VirtualNetworkName
<string>] [-AddressPrefix <string>] [-SubnetName <string>] [-
SubnetAddressPrefix <string>] [-PublicIpAddressName <string>] [-
DomainNameLabel <string>] [-AllocationMethod <string>] [-
SecurityGroupName <string>] [-OpenPorts <int[]>] [-Image <string>] [-
Size <string>] [-AvailabilitySetName <string>] [-SystemAssignedIdentity]
[-UserAssignedIdentity <string>] [-AsJob] [-DataDiskSizeInGb <int[]>] [-
EnableUltraSSD] [-ProximityPlacementGroup <string>] [-HostId <string>]
[-DefaultProfile <IAzureContextContainer>] [-WhatIf] [-Confirm]
[<CommonParameters>]
New-AzVM [-ResourceGroupName] <string> [-Location] <string> [-VM]
<PSVirtualMachine> [[-Zone] <string[]>] [-DisableBginfoExtension] [-Tag
<hashtable>] [-LicenseType <string>] [-AsJob] [-DefaultProfile
<IAzureContextContainer>] [-WhatIf] [-Confirm] [<CommonParameters>]
New-AzVM -Name <string> -DiskFile <string> [-ResourceGroupName <string>] [-
Location <string>] [-VirtualNetworkName <string>] [-AddressPrefix
<string>] [-SubnetName <string>] [-SubnetAddressPrefix <string>] [-
PublicIpAddressName <string>] [-DomainNameLabel <string>] [-
AllocationMethod <string>] [-SecurityGroupName <string>] [-OpenPorts
<int[]>] [-Linux] [-Size <string>] [-AvailabilitySetName <string>] [-
SystemAssignedIdentity] [-UserAssignedIdentity <string>] [-AsJob] [-
DataDiskSizeInGb <int[]>] [-EnableUltraSSD] [-ProximityPlacementGroup
<string>] [-HostId <string>] [-DefaultProfile <IAzureContextContainer>]
[-WhatIf] [-Confirm] [<CommonParameters>]
Обратили внимание на знакомый параметр? -AsJob
! Давайте посмотрим что делается в
этой команде:
PS /Users/travisp/> Get-Help New-AzVM -Parameter AsJob
-AsJob <System.Management.Automation.SwitchParameter>
Run cmdlet in the background and return a Job to track progress.
Required? false
Position? named
Default value False
Accept pipeline input? False
Accept wildcard characters? false
Этот параметр просит New-AzVM
вернуть некое Job
.
Если мы выпалим этот командлет после того, как поместим имя пользователя и пароль для своей виртуальной машины, мы обнаружим,
что получили обратно Job
.
PS /Users/travisp/> New-AzVm -Name myawesomevm -Image UbuntuLTS -AsJob
cmdlet New-AzVM at command pipeline position 1
Supply values for the following parameters:
Credential
User: azureuser
Password for user azureuser: ***********
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
8 Long Running O... AzureLongRunni... Running True localhost New-AzVM
Что превращает это в настолько удивительное, так это то, что вы можете управлять этими заданиями так же, как и заданиями,
которые возвращались Start-Job
или
Start-ThreadJob
. Позднее вы увидите как мы обходимся с управлением заданиями, однако
это некий пример того как может проявляться персонализация заданий. Ищите параметр
-AsJob
!
Самая первая вещь, которую вы скорее всего пожелаете сделать после запуска задания, это проверить закончилось ли
это задание. Командлет Get-Job
выполняет выборку каждого задания, определённого
вашей системой и отображает вам состояние каждого:
PS /Users/travisp/> get-job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Completed True localhost gci
3 Job3 BackgroundJob Completed True localhost gci
5 Job5 ThreadJob Completed True PowerShell gci
8 Job8 BackgroundJob Completed True server-r2, lo...
11 MyRemoteJob BackgroundJob Completed True server-r2, lo...
13 Long Running O... AzureLongRunni... Running True localhost New-AzVM
Вы также имеете возможность выполнить выборку определённого задания воспользовавшись его идентификатором или его названием.
Мы предполагаем, что вы делаете это и отправляете конвейером в Format-List *
, потому как
вы собрали ценные сведения:
PS /Users/travisp/> get-job -id 1 | format-list *
State : Completed
HasMoreData : True
StatusMessage :
Location : localhost
Command : gci
JobStateInfo : Completed
Finished : System.Threading.ManualResetEvent
InstanceId : e1ddde9e-81e7-4b18-93c4-4c1d2a5c372c
Id : 1
Name : Job1
ChildJobs : {Job2}
PSBeginTime : 12/12/2019 7:18:58 PM
PSEndTime : 12/12/2019 7:18:58 PM
PSJobTypeName : BackgroundJob
Output : {}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
Information : {}
Испробуйте это сейчас
Если вы следите за нами, имейте в виду, что ваши идентификаторы и названия заданий могут отличаться от наших. Чтобы сделать
выборку своих идентификаторов и названий заданий, сосредоточьтесь на выводе Get-Job
и
подставьте их в своих примерах. Также имейте в виду, что Microsoft в последних нескольких версиях PowerShell Microsoft
расширила свой объект задания, поэтому ваши результаты при просмотре всех свойств могут отличаться.
Свойство ChildJobs
это один из самых сажных моментов в сведениях и мы рассмотрим его
через мгновение. Для выборки полученных результатов из задания пользуйтесь Receive-Job
,
однако прежде чем вы исполните его, вам необходимо узнать несколько вещей:
-
Вам придётся определить то задание из которого вы желаете получать результаты. Вы можете делать это по идентификатору задания или по имени задания, либо получать задания при помощи
Get-Job
и отправлять конвейером их вReceive-Job
. -
Когда вы получаете необходимые результаты из своего родительского задания, эти результаты будут включать весь вывод из всех дочерних заданий. В качестве альтернативы, вы можете выбрать получение результатов от одного или более дочерних заданий.
-
Обычно, получение необходимых результатов из некого задания вычищает их из кэша вывода этого задания, поэтому вы не сможете получить их повторно. Для удержания копии этих результатов в памяти, определите
-keep
. Или же, когда вы желаете оставлять некую копию для работы с нею, вы можете выдавать получаемые результаты в некий файл CLIXML. -
Результаты вашего задания могут быть распакованными объектами, которые вы изучали в Главе 13. Это моментальные снимки в тот момент времени, когда они были выработаны и они могут не обладать никакими методами, которые вы способны выполнять. Однако, если пожелаете, вы можете отправлять конвейером результаты своего задания непосредственно в такие командлеты как
Sort-Object
,Format-List
,Export-CSV
,ConvertTo-HTML
,Out-File
и тому подобные.
Вот некий образец:
PS /Users/travisp/> receive-job -id 1
Directory: /Users/travisp
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 11/24/2019 10:53 PM Code
d---- 11/18/2019 11:23 PM Desktop
d---- 9/15/2019 9:12 AM Documents
d---- 12/8/2019 11:04 AM Downloads
d---- 9/15/2019 7:07 PM Movies
d---- 9/15/2019 9:12 AM Music
d---- 9/15/2019 6:51 PM Pictures
d---- 9/15/2019 9:12 AM Public
Наш предыдущий вывод отображает занимательный набор результатов. Вот быстрое напоминание о той команде, которая запускала данное задание в его первом месте:
PS /Users/travisp/> start-job -scriptblock { gci }
Когда мы получили необходимые результаты от Job1
, мы не определили
-keep
. Если мы попытаемся снова получить эти результаты, мы не получим ничего, потому
как эти результаты более не кэшированы в своём задании:
PS /Users/travisp/> receive-job -id 1
Вот как мы можем заставлять эти результаты оставаться кэшированными в памяти:
PS /Users/travisp/> receive-job -id 3 –keep
Directory: /Users/travisp
Mode LastWriteTime Length Name
---- ------------- ------ ----
d---- 11/24/2019 10:53 PM Code
d---- 11/18/2019 11:23 PM Desktop
d---- 9/15/2019 9:12 AM Documents
d---- 12/8/2019 11:04 AM Downloads
d---- 9/15/2019 7:07 PM Movies
d---- 9/15/2019 9:12 AM Music
d---- 9/15/2019 6:51 PM Pictures
d---- 9/15/2019 9:12 AM Public
В конечном счёте вы пожелаете освободить свою память, которая применяется для кэширования результатов вашего задания, и мы слегка коснёмся этого. Но вначале давайте взглянем на быстрый образец отправления конвейером результатов задания непосредственно в другой командлет:
PS /Users/travisp> receive-job -name myremotejob | sort-object PSComputerName | Format-Table -groupby PSComputerName
PSComputerName: localhost
NPM(K) PM(M) WS(M) CPU(s) Id ProcessName PSComputerName
------ ----- ----- ------ -- ----------- --------------
0 0 56.92 0.70 484 pwsh localhost
0 0 369.20 70.17 1244 Code localhost
0 0 71.92 0.20 3492 pwsh localhost
0 0 288.96 15.31 476 iTerm2 localhost
Это было то задание, которое мы запустили при помощи Invoke-Command
. Наш командлет
был добавлен свойством PSComputerName
, поэтому мы можем продолжать отслеживать какой
объект возвращается от какого компьютера. Поскольку мы выполняем выборку получаемых результатов от своего задания верхнего
уровня, они содержат определёнными все свои компьютеры, что позволяет этой команде сортировать их по значению названия
компьютера и затем создавать некую индивидуальную группу таблиц для каждого компьютера. Get-Job
к тому же способен поддерживать для вас сведения о том, для каих заданий остаются результаты:
PS /Users/travisp> get-job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Completed False localhost gci
3 Job3 BackgroundJob Completed True localhost gci
5 Job5 ThreadJob Completed True PowerShell gci
8 Job8 BackgroundJob Completed True server-r2, lo...
11 MyRemoteJob BackgroundJob Completed False server-r2, lo...
13 Long Running O... AzureLongRunni... Running True localhost New-AzVM
Значением колонки HasMoreData
будет False
,
когда нет кэшированного вывода для этого задания. В случае Job1
и
MyRemoteJob
мы уже получили эти результаты и на данный момент не определяли
-keep
.
Ранее мы упоминали что большинство заданий состоит из одного родительского задания верхнего уровня и по крайней мере одного дочернего задания. Давайте снова взглянем на задание:
PS /Users/travisp> get-job -id 1 | format-list *
State : Completed
HasMoreData : True
StatusMessage :
Location : localhost
Command : dir
JobStateInfo : Completed
Finished : System.Threading.ManualResetEvent
InstanceId : e1ddde9e-81e7-4b18-93c4-4c1d2a5c372c
Id : 1
Name : Job1
ChildJobs : {Job2}
PSBeginTime : 12/27/2019 2:34:25 PM
PSEndTime : 12/27/2019 2:34:29 PM
PSJobTypeName : BackgroundJob
Output : {}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
Information : {}
Испробуйте это сейчас
Не следуйте за этой частью, потому как если вы до сих пор следовали ею, вы уже получили результаты Job1
.
Если вы хотите это попробовать, запустите новое задание выполнив Start-Job -script { dir }
и воспользовавшись идентификатором этого нового задания вместо того номера идентификатора 1, которым мы применили в своём
примере.
Вы можете обнаружить, что Job1
обладает дочерним заданием,
Job2
. Вы можете получить его теперь непосредственно, ибо знаете его название:
PS /Users/travisp> get-job -name job2 | format-list *
State : Completed
StatusMessage :
HasMoreData : True
Location : localhost
Runspace : System.Management.Automation.RemoteRunspace
Debugger : System.Management.Automation.RemotingJobDebugger
IsAsync : True
Command : dir
JobStateInfo : Completed
Finished : System.Threading.ManualResetEvent
InstanceId : a21a91e7-549b-4be6-979d-2a896683313c
Id : 2
Name : Job2
ChildJobs : {}
PSBeginTime : 12/27/2019 2:34:25 PM
PSEndTime : 12/27/2019 2:34:29 PM
PSJobTypeName :
Output : {Applications, Code, Desktop, Documents, Downloads, Movies, Music...}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
Information : {}
Порой задание обладает настолько большим числом дочерних заданий для перечисления в этой форме, что вы можете пожелать перечислять их несколько иначе, следующим образом:
PS /Users/travisp> get-job -id 1 | select-object -expand childjobs
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
2 Job2 Completed True localhost gci
Данная техника создаёт таблицу имеющихся дочерних заданий для задания с идентификатором 1 и эта таблица может быть какой
угодно длины, необходимой для перечисления их всех. Вы можете получать результаты любого отдельного дочернего задания,
указывая его название или идентификатор при помощи команды Receive-Job
.
Задания также применяют ещё три дополнительные команды. Для каждой из них вы можете определять задание либо по указанию его идентификатора, либо по его имени, или же получать необходимые задания и отправляя их конвейером в один из таких командлетов:
-
Remove-Job
- Он удаляет из памяти задание и весь вывод всё ещё кэшируемый им. -
Stop-Job
- Когда некое задание кажется подвисшим, данная команда прекращает его. Вы всё ещё имеете возможность получения каких- либо выработанных к данному моменту результатов. -
Wait-Job
- Это полезно когда некий сценарий намерен запустить задание или задания и вы хотите чтобы данный сценарий продолжался лишь тогда, когда исполнится такое задание. Данная команда принуждает вашу оболочку остановиться и ожидать выполнения своего задания (или заданий), а затем позволяет вашей оболочке продолжить исполнение.
Например, для удаления своего задания, от которого мы уже получили вывод, мы бы воспользовались следующей командой:
PS /Users/travisp> get-job | where { -not $_.HasMoreData } | remove-job
PS /Users/travisp> get-job
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
3 Job3 BackgroundJob Completed True localhost gci
5 Job5 ThreadJob Completed True PowerShell gci
8 Job8 BackgroundJob Completed True server-r2, lo...
13 Long Running O... AzureLongRunni... Completed True localhost New-AzVM
Задания также способны завершаться неудачно, что означает что нечто пошло не так при его выполнении. Рассмотрим такой пример:
PS /Users/travisp> invoke-command -command { nothing } -hostname notonline -asjob -jobname ThisWillFail
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
11 ThisWillFail BackgroundJob Failed False notonline nothing
В данном случае мы запустили задание с фиктивной командой и указали целью не существующий компьютер. Данное задание
немедленно падает, что указано в его состоянии. Нам нет нужды применять на этот раз Stop-Job
;
это задание не исполняется. Однако мы можем получить список его дочерних заданий:
PS /Users/travisp> get-job -id 11 | format-list *
State : Failed
HasMoreData : False
StatusMessage :
Location : notonline
Command : nothing
JobStateInfo : Failed
Finished : System.Threading.ManualResetEvent
InstanceId : d5f47bf7-53db-458d-8a08-07969305820e
Id : 11
Name : ThisWillFail
ChildJobs : {Job12}
PSBeginTime : 12/27/2019 2:45:12 PM
PSEndTime : 12/27/2019 2:45:14 PM
PSJobTypeName : BackgroundJob
Output : {}
Error : {}
Progress : {}
Verbose : {}
Debug : {}
Warning : {}
Information : {}
Далее мы можем получить такое дочернее задание:
PS /Users/travisp> get-job -name job12
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
12 Job12 Failed False notonline nothing
Как вы можете наблюдать, для этого задания не было создано никакого вывода, а потому у вас нет никаких результатов для их
выборки. Однако ошибки этого задания сохранены в полученных результатах и вы имеете возможность получить их воспользовавшись
Receive-Job
:
PS /Users/travisp> receive-job -name job12
OpenError: [notonline] The background process reported an error with the following message: The SSH client session has ended with error message: ssh: Could not resolve hostname notonline: nodename nor servname provided, or not known.
Текст полной ошибки намного длиннее; мы усекли его здесь для сохранения пространства. Вы заметите, что данная ошибка включает
значение имени хоста, с которого пришла эта ошибка, [notonline]
. Что произойдёт, если
лишь один из компьютеров не может быть достигнут? Давайте попробуем:
PS /Users/travisp> invoke-command -command { nothing } -computer notonline,server-r2 -asjob -jobname ThisWilLFail
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- --------- -------- -------
13 ThisWillFail BackgroundJob Running True notonline,lo... nothing
После короткого ожидания мы выполним следующее:
PS /Users/travisp> get-job 13
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
13 ThisWillFail BackgroundJob Failed False notonline,lo... nothing
Наше задание всё ещё отказавшее, но давайте взглянем на индивидуальные дочерние задания:
PS /Users/travisp> get-job -id 13 | select -expand childjobs
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
14 Job14 Failed False notonline nothing
15 Job15 Failed False localhost nothing
ладно, они упали оба. Мы имеем ощущение, что мы знаем почему не работает Job14
,
однако что не верно с Job15
?
PS /Users/travisp> receive-job -name job15
Receive-Job : The term 'nothing' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
А, всё верно, мы попросили его выполнить не существующую команду. Как вы можете видеть, каждое дочернее задание способно отказывать по различным причинам и PowerShell отслеживает каждую из них индивидуально.
Задания обычно просты, однако мы встречали парней, которые делают одну вещь, которая вызывает путаницу. Не делайте этого:
PS /Users/travisp> invoke-command -command { Start-Job -scriptblock { dir } } -hostname Server-R2
Это запускает временное подключение к Server-R2
и запускает локальное задание.
К сожалению, такое подключение немедленно разрывается, поэтому у вас нет возможности повторного подключения и получения такого
задания. В общем, не смешивайте и не сочетайте три способа запуска заданий. Ниже приводится также плохая идея:
PS /Users/travisp> start-job -scriptblock { invoke-command -command { dir } -hostname SERVER-R2 }
Это полностью избыточно; оставьте свой раздел Invoke-Command
и воспользуйтесь его
параметром -AsJob
чтобы получить это исполняемым в фоновом режиме.
Менее сбивающими с толку, но также интересными являются те вопросы, которые новые пользователи часто задают о заданиях. Вероятно, наиболее важный из них: "можем ли мы наблюдать задания, запущенные кем- то ещё?" Правильный ответ - нет. Задания и потоковые задания целиком содержатся внутри самого процесса PowerShell и, хотя вы и можете обнаруживать что другой пользователь запускает PowerShell, вы не способны заглянуть вовнутрь этого процесса. Это похоже на любое иное приложение: например, вы можете наблюдать, что другой пользователь работает с Microsoft Word, но вы не можете видеть какие именно документы изменяет этот пользователь, потому как эти документы полностью заключены вовнутрь процесса Word.
Задания выполнятся лишь до тех пор, пока открыт ваш сеанс PowerShell. После его закрытия все определённые в нём задания исчезают. Задания не определены где- либо вне PowerShell, поэтому они зависят от того, что их процесс продолжает исполняться для поддержания себя.
Приводимые далее упражнения должны помочь вам разобраться с тем как работать с различными типами заданий и задач в PowerShell. Работая с этими упражнениями, не думайте что вам следует написать решение в одну строку. Иногда проще разбивать задачу на отдельные этапы.
-
Создайте одноразовое потоковое задание для поиска всех текстовых файлов (
*.txt
) в вашей файловой системе. Любые задачи, которые могут потребовать длительного времени для своего завершения это отличные претенденты для задания. -
Вы осознали, что было бы полезным идентифицировать все текстовые файлы в некоторых из ваших серверов. Как бы вы могли исполнить ту ж самую команду из задачи 1 в некой группе удалённых компьютеров?
-
Каким бы командлетом вы бы воспользовались для получения результатов задания и как бы вы сохранили эти результаты в своей очереди заданий?
-
Start-ThreadJob {gci / -recurse –filter '*.txt'}
-
Invoke-Command –scriptblock {gci / -recurse –filter *.txt} –computername (get-content computers.txt) -asjob
-
Receive-Job –id 1 –keep
Естественно, вы бы могли воспользоваться тем идентификатором задания, который подходит вам, или же его именем задания.