Фоновые задания выполняются в отдельном (дочернем) процессе, поэтому вы принципиально не можете напрямую обновлять значения в области действия вызывающего из них. [1]
Вместо этого заставьте свой блок сценария задания выдавать вывод , который вызывающий абонент может записать с помощью Receive-Job
.
Простой пример:
# Create a 10-element array in a background job and output it.
# Receive-Job collects the output.
$arrayFromJob = Start-Job { 1..10 } | Receive-Job -Wait -AutoRemoveJob
Примечание. Если то, что вы выводите из фонового задания, является сложным объектом, они, как правило, , а не сохраняют свой первоначальный тип и вместо этого являются пользовательским объектом эмуляции , из-за ограничений PowerShell *. Инфраструктура межпроцессной сериализации на основе 1139 *; только ограниченный набор известных типов десериализуется с верностью типов, включая примитивные типы. NET, хеш-таблицы и экземпляры [pscustomobject]
(с ограничениями верности типов, вновь применяемыми к их свойствам и записям). - см. этот ответ для справочной информации.
Несколько сторон:
Нет необходимости звонить Start-Job
/ Get-WmiObject
в al oop, потому что последний -ComputerName
Параметр может принимать массив целевых компьютеров для подключения за один вызов.
- Поскольку целевые компьютеры запрашиваются параллельно , вы не можете вообще нужно фоновое задание (
Start-Job
).
Командлеты CIM (например, Get-CimInstance
) заменили командлеты WMI (например, Get-WmiObject
) в PowerShell v3 (выпущен в сентябре 2012 г.). Поэтому следует избегать командлетов WMI , не в последнюю очередь потому, что PowerShell [Core] (версия 6 и выше), где все будущие усилия будут go, даже не имеет их больше.
- Удаленное использование командлетов CIM по умолчанию требует, чтобы целевые компьютеры были настроены для подключений WS-Management, как это неявно происходит, если на них включено удаленное взаимодействие PowerShell - см. about_Remote_Requirements ; в качестве альтернативы, однако, вы можете использовать протокол DCOM (который используется для командлетов WMI) - см. этот ответ для получения дополнительной информации.
Применение выше для вашего случая:
# Create a CIM session that targets all computers.
# By default, the WS-Management protocol is used, which target computers
# are implicitly set up for if PowerShell remoting is enabled on them.
# However, you can opt to use DCOM - as the WMI cmdlets did - as follows:
# -SessionOption (New-CimSessionOption -Protocol DCOM)
$session = New-CimSession -ComputerName $computers -Credential $Cred
# Get the CIM data from all target computers in parallel.
[array] $cimData = Get-CimInstance -CimSession $session -Class win32_computersystem -ErrorVariable Err -ErrorAction SilentlyContinue |
ForEach-Object {
[pscustomobject] @{
Domain = $_.Domain
Manufacturer = $_.Manufacturer
Computer = $_.ComputerName
Name = $_.Name
}
}
# Cleanup: Remove the session.
Remove-CimSession $session
# Add error information, if any.
if ($Err) {
Set-Content D:\InventoryError.log -Force -Value $Err
$cimData += $Err | ForEach-Object {
[pscustomobject] @{
Domain = "Error"
Manufacturer = "Error"
Computer = $_.ComputerName
Name = "Error"
}
}
}
Предостережение по поводу одновременного таргетинга на большое количество компьютеров :
На момент написания статьи ни Get-CimInstance
help topi c и концептуальные about_CimSession topi c обсуждение регулирование соединения (ограничение количества одновременных подключений к удаленным компьютерам для предотвращения чрезмерной нагрузки система).
Универсальная команда PowerShell Invoke-Command
Команда удаленного взаимодействия, напротив, имеет параметр -ThrottleLimit
, который по умолчанию равен 32
. Обратите внимание, что удаленное взаимодействие PowerShell должно сначала быть включено на целевых компьютерах, чтобы иметь возможность удаленно использовать Invoke-Command
на них - см. about_Remote_Requirements .
Поэтому, чтобы иметь больший контроль над тем, как компьютеры ориентированы параллельно, рассмотрите возможность объединения Invoke-Command
с local вызовом Get-CimInstance
на каждой удаленной машине ; например:
Invoke-Command -ComputerName $computers -ThrottleLimit 16 {
Get-CimInstance win32_computersystem
} -Credential $Cred -ErrorVariable Err -ErrorAction
Также передается объект * options-options параметру Invoke-Command
-SessionOption
, созданный с помощью New-PSSessionOption
, дополнительно дает Вы управляете различными таймаутами .
[1] В блоке сценария, выполняемом в фоновом задании, переменная automati c $args
содержит десериализованный копии значений, переданных вызывающей стороной - см. этот ответ для получения справочной информации.
Обратите внимание, что обычно предпочтительный командлет Start-ThreadJob
на основе потоков - см. этот ответ - может получать живые ссылки на экземпляры ссылочного типа в области вызова, хотя изменяет таких объектов, тогда требуется явная синхронизация, если несколько потоковых заданий получают к ним доступ параллельно; то же самое относится к функции PowerShell 7+ ForEach-Object -Parallel
.