2014-02-19 55 views
2

我有以下脚本不被枚举对象时:错误修改

$serverList = @{ 
    "Server1Name" = @{ "WindowsService1" = "Status"; "WindowsService2" = "Status" }; 
    "Server2Name" = @{ "WindowsService1" = "Status"; "WindowsService2" = "Status" }; 
    "Server3Name" = @{ "WindowsService1" = "Status" }; 
    "Server4Name" = @{ "WindowsService1" = "Status" }; 
    "Server5Name" = @{ "WindowsService1" = "Status" }; 
    "Server6Name" = @{ "WindowsService1" = "Status" } 
} 

$copy = $serverList.Clone() 

foreach ($server in $copy.Keys) { 
    foreach ($service in $copy[$server].Keys) { 
     $serviceInfo = Get-Service -ComputerName $server -Name $service 
     $serverList[$server][$service] = $serviceInfo.Status 
    } 
} 

我确信,我没有修改被列举的哈希表,但是当我运行但我仍然得到这个错误剧本:

Collection was modified; enumeration operation may not execute.At line:14 char:14 
+  foreach ($service in $copy[$server].Keys) { 
+    ~~~~~~~~ 
    + CategoryInfo   : OperationStopped: (:) [], InvalidOperationException 
    + FullyQualifiedErrorId : System.InvalidOperationException 

我在这里读到:http://blog.matticus.net/2013/11/powershell-clearing-values-within.html。如果我在那里复制代码表单,它对我来说执行没有错误。

我的问题可能与嵌套的foreach循环有关吗?我的代码中有错误吗?任何人都可以对此有所了解吗?

回答

3

Powershell不喜欢你正在修改你正在迭代的集合。

在开始时,您创建了一个名为$ copy的克隆以避免此问题。 clone()是一个“浅拷贝”,因此对于每个键引用的对象在副本中都是相同的。

在此行中:

$serverList[$server][$service] = $serviceInfo.Status 

您修改收集 - 您当前迭代。

实际上,outter集合从来没有被修改过,只是提到,所以outter clone()调用是不必要的。相反,你应该克隆内部集合。 像这样(未经):

$serverList = @{ 
    "Server1Name" = @{ "WindowsService1" = "Status"; "WindowsService2" = "Status" }; 
    "Server2Name" = @{ "WindowsService1" = "Status"; "WindowsService2" = "Status" }; 
    "Server3Name" = @{ "WindowsService1" = "Status" }; 
    "Server4Name" = @{ "WindowsService1" = "Status" }; 
    "Server5Name" = @{ "WindowsService1" = "Status" }; 
    "Server6Name" = @{ "WindowsService1" = "Status" } 
} 



foreach ($server in $serverList.Keys) { 
    $copy = $serverList[$server].clone(); 
    foreach ($service in $copy.Keys) { 
     $serviceInfo = Get-Service -ComputerName $server -Name $service 
     $serverList[$server][$service] = $serviceInfo.Status 
    } 
} 
+0

哇,漂亮的答案,以取代线

$copy = $serverList.Clone() 

。像魅力一样工作。 –

+0

感谢您的解决方法。仅仅是我,还是这是PowerShell的一个坏主题?我希望它克隆整个对象,而不仅仅是它的一部分...... –

+0

不,这是底层框架处理集合的方式所固有的。你会在.Net中遇到同样的问题(实际上,我更像是一个C#人,但我知道PowerShell的工作原理是一样的)。天真地说,你并没有改变“Keys”属性,所以它应该没问题。但是,.Keys可能是对KeyValue对象的迭代,并且您正在更改KeyValueObject.Value - 并且框架因此认为您已修改了正在迭代的集合。 – Tewr

1

我感到惊讶的是.Clone()方法只创建一个新的参照同一个对象,它不会创建具有相同属性的新对象。我无法找到实际复制整个散列表的简单方法,而不是克隆它。所以我写了一个函数来做到这一点:

Function Copy-HashTable($HashTable) { 
    $newHash = @{} 
    $HashTable.GetEnumerator() | ForEach-Object { 
     if ($_.Value -is "Hashtable") { 
      $newHash[$_.Key] = Copy-HashTable $_.Value 
     } else { 
      $newHash[$_.Key] = $_.Value 
     } 
    } 
    $newHash 
} 

将此应用于你的代码,你只需要

$copy = Copy-HashTable $ServerList 
+0

+1使用递归 –