2014-02-19 88 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使用遞歸 –