Loops 修改未枚举的对象时出错

Loops 修改未枚举的对象时出错,loops,powershell,foreach,hashtable,Loops,Powershell,Foreach,Hashtable,我有以下脚本: $serverList = @{ "Server1Name" = @{ "WindowsService1" = "Status"; "WindowsService2" = "Status" }; "Server2Name" = @{ "WindowsService1" = "Status"; "WindowsService2" = "Status" }; "Server3Name" = @{ "WindowsService1" = "Status" };

我有以下脚本:

$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
我在这里读到了:。如果我将代码表单复制到那里,它将在没有错误的情况下执行


我的问题可能与嵌套的foreach循环有关吗?我的密码有错误吗?有人能解释一下吗?

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

开始时,为了避免此问题,您制作了一个名为$copy的克隆。克隆是一个浅拷贝,因此每个键引用的对象在副本中都是相同的

在这一行:

$serverList[$server][$service] = $serviceInfo.Status
您可以修改当前正在迭代的内部集合

事实上,outter集合从不被修改,只被引用,因此outter克隆调用是不必要的。相反,您应该克隆内部集合。 类似这样的未经测试:

$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
    }
}

Powershell不希望您修改正在迭代的集合

开始时,为了避免此问题,您制作了一个名为$copy的克隆。克隆是一个浅拷贝,因此每个键引用的对象在副本中都是相同的

在这一行:

$serverList[$server][$service] = $serviceInfo.Status
您可以修改当前正在迭代的内部集合

事实上,outter集合从不被修改,只被引用,因此outter克隆调用是不必要的。相反,您应该克隆内部集合。 类似这样的未经测试:

$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
    }
}

我很惊讶.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()


我很惊讶.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()


哇,回答得好。工作很有魅力。谢谢你的解决办法。只是我,还是这是PowerShell的一个糟糕功能?我希望它克隆整个对象,而不仅仅是它的一部分……不,这是底层框架处理集合的固有方式。事实上,在.Net中你也会遇到同样的问题,我更喜欢c语言,但我知道powershell的工作原理是一样的。天真地说,您没有更改Keys属性,因此应该可以。但是,.Keys可能是对KeyValue对象的迭代,您正在更改KeyValueObject.Value-因此框架认为您已经修改了您正在迭代的集合。哇,回答不错。工作很有魅力。谢谢你的解决办法。只是我,还是这是PowerShell的一个糟糕功能?我希望它克隆整个对象,而不仅仅是它的一部分……不,这是底层框架处理集合的固有方式。事实上,在.Net中你也会遇到同样的问题,我更喜欢c语言,但我知道powershell的工作原理是一样的。天真地说,您没有更改Keys属性,因此应该可以。但是,.Keys可能是对KeyValue对象的迭代,您正在更改KeyValueObject.Value,因此框架认为您已经修改了正在迭代的集合。