修改未枚举的对象时出错

时间:2014-02-19 15:33:28

标签: loops powershell foreach hashtable

我有以下脚本:

$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循环有关吗?我的代码中有错误吗?任何人都可以对此有所了解吗?

2 个答案:

答案 0 :(得分:3)

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

一开始你创建了一个名为$ copy的克隆来避免这个问题。 clone()是一个“浅拷贝”,因此每个密钥所指的对象在您的副本中是相同的。

在这一行:

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

您修改内部集合 - 您当前正在迭代它。

实际上,外部集合永远不会被修改,只会被引用,因此外部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
    }
}

答案 1 :(得分: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 = $serverList.Clone()

$copy = Copy-HashTable $ServerList