Art*_*lle 5 powershell foreach-object
我正在尝试使用 ForEach-Object -Parallel 从多个服务器收集数据。我使用的变量正在循环内填充,但是当循环完成时该变量为空。
$DBDetails = "SELECT @@VERSION"
$VMs = ("vm1", "vm2", "vm3", "vm4", "vm5", "vm6", "vm7")
$DBInventory = @()
$scriptBlock = {
$vm = $_
$result = Invoke-Sqlcmd -ServerInstance $vm -Query $using:DBDetails
$DBInventory += $result
Write-Host "Added $($result.Count) rows from $($vm)"
}
$VMs | ForEach-Object -Parallel $scriptBlock
Write-Host "Number of elements in DBInventory: $($DBInventory.Count)"
Run Code Online (Sandbox Code Playgroud)
我希望最后一行返回在前一行执行的循环中收集的元素数量。总共应该有 7 个元素,但我一个都没有。
我的结果如下所示:
Added 1 rows from vm1
Added 1 rows from vm2
Added 1 rows from vm3
Added 1 rows from vm4
Added 1 rows from vm5
Added 1 rows from vm6
Added 1 rows from vm7
Number of elements in DBInventory: 0
Run Code Online (Sandbox Code Playgroud)
ForEach-Object -Parallel导致循环体在单独的运行空间中执行,这意味着您无法直接访问调用范围中定义的变量。
要解决此问题,请对代码进行两处更改:
[List[psobject]])using:,并将其分配给块内的局部变量然后,生成的局部变量将引用内存中的相同列表对象,并且通过其方法(Add()、Remove()、AddRange()等)对该列表所做的更改将反映在其引用的任何其他位置(包括$DBInventory调用范围中的原始变量)。
$DBDetails = "SELECT @@VERSION"
$VMs = ("vm1", "vm2", "vm3", "vm4", "vm5", "vm6", "vm7")
$DBInventory = [System.Collections.Generic.List[psobject]]::new()
$scriptBlock = {
$vm = $_
$inventory = $using:DBInventory
$result = Invoke-Sqlcmd -ServerInstance $vm -Query $using:DBDetails
$inventory.AddRange([psobject[]]$result)
Write-Host "Added $($result.Count) rows from $($vm)"
}
$VMs | ForEach-Object -Parallel $scriptBlock
Write-Host "Number of elements in DBInventory: $($DBInventory.Count)"
Run Code Online (Sandbox Code Playgroud)
正如mklement0 所指出的那样,[List[psobject]]它不是线程安全的- 对于生产代码,您肯定会想要选择一个集合类型,例如 a [System.Collections.Concurrent.ConcurrenBag[psobject]]- 本质上是一个无序列表:
$DBInventory = [System.Collections.Concurrent.ConcurrentBag[psobject]]::new()
Run Code Online (Sandbox Code Playgroud)
请注意,ConcurrentBag顾名思义,该类型不保留插入顺序。如果这是一个问题,您可能需要考虑使用[ConcurrentDictionary[string,psobject[]]]- 这样您可以将查询输出绑定回原始输入字符串:
$DBInventory = [System.Collections.Concurrent.ConcurrentDictionary[string,psobject[]]]::new()
Run Code Online (Sandbox Code Playgroud)
由于自从您将调用分派到 以来,另一个线程可能(假设)已经添加了同一键的条目Add(),因此该ConcurrentDictionary类型要求我们使用它与常规字典或哈希表略有不同:
$scriptBlock = {
$vm = $_
$inventory = $using:DBInventory
$result = Invoke-Sqlcmd -ServerInstance $vm -Query $using:DBDetails
$adder = $updater = { return Write-Output $result -NoEnumerate }
$inventory.AddOrUpdate($vm, $adder, $updater)
Write-Host "Added $($result.Count) rows from $($vm)"
}
Run Code Online (Sandbox Code Playgroud)
$adder在这里,如果键不存在,并发字典将代表我们执行该函数(否则它将运行该函数$updater),并且结果将被分配为条目值。
随后您可以像访问哈希表一样访问条目值:
$DBInventory[$vms[-1]] # returns array containing the query results from the last VM in the list
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
808 次 |
| 最近记录: |