我想要实现一个简单的目标。在多台服务器上按定义的顺序停止一些服务,然后按相反的顺序启动它们。

因此,第一步是定义一个有序的哈希表/字典。

$serviceServer = [ordered]@{
    "service3" = @("server1")
    "service1" = @("server2", "server3")
    "service2" = @("server1", "server2", "server3")
}

但是我如何逆转服务重新启动的顺序呢?

在 php 中我会做一个 array_reverse …没什么大不了的,但 Powershell 似乎有它自己的简单含义。

我尝试了多种方式对 Sort-Object 进行排序,但顺序保持不变。

$reverseServices = $serviceServer | Sort-Object

看看这个并自己尝试一下,我已经做了一些性能测试。

$serviceServers = [ordered]@{
    "service3" = @("server1")
    "service1" = @("server2", "server3")
    "service2" = @("server1", "server2", "server3")
    "service6" = @("server1")
    "service4" = @("server2", "server3")
    "service5" = @("server1", "server2", "server3")
    "service9" = @("server1")
    "service7" = @("server2", "server3")
    "service8" = @("server1", "server2", "server3")
}

function Reverse-ByEnum1 {
    param(
        $serviceServer
    )
    $keys = @($serviceServer.Keys)
    $serviceServer.GetEnumerator() | Sort-Object { $keys.IndexOf($_.Key) } -Descending
}

function Reverse-ByEnum2 {
    param(
        $serviceServer
    )
    $serviceServer.GetEnumerator() `
        | foreach-object `
            -begin   { $r = [ordered] @{} } `
            -process { $r.Insert(0, $_.Key, $_.Value) } `
            -end     { $r }
}

function Reverse-ByKeys {
    param(
        $serviceServer
    )
    $reversedServiceServer = [ordered] @{}
    foreach ($key in $serviceServer.Keys) {
        $reversedServiceServer.Insert(0, $key, $serviceServer[$key]) 
    }
    $reversedServiceServer
}

Measure-Command {
    for ($i = 0; $i -lt 10000; $i++) {
        $reverse1 = Reverse-ByEnum1 $serviceServers
    }
}

Measure-Command {
    for ($i = 0; $i -lt 10000; $i++) {
        $reverse2 = Reverse-ByEnum2 $serviceServers
    }
}

Measure-Command {
    for ($i = 0; $i -lt 10000; $i++) {
        $reverse3 = Reverse-ByKeys $serviceServers
    }
}

Write-Host =====================================================================
$reverse1
Write-Host =====================================================================
$reverse2
Write-Host =====================================================================
$reverse3

6

  • 1
    我现在无法测试,所以不作为答案发布,但 OrderedDictionary 有一个“Insert”方法,您可以使用它来迭代所有现有键,然后将其“插入”到索引为零的新 OrderedDictionary 中。这将反转新 OrderedDictionary 中的顺序并将其保留在原始顺序中。


    – 


  • @elh:评估解决方案时需要考虑的权衡因素有:是否需要构建一个新的有序哈希表,或者以相反的顺序枚举条目是否足够?性能重要吗?基于管道的解决方案可能更简洁、更优雅,但相对较慢 – 但对于较小的哈希表来说,这并不重要。


    – 

  • 我喜欢为其他人提供性能和可读性,因为他们没有我这样的经验。所以为这个问题设计一个钥匙扣可能不错。就像设计模式一样。… 我不想那么快就得到解决方案。可能还有其他一些针对常见问题的好主意。


    – 


  • 1
    但我想澄清一下你和其他人的答案的质量。谢谢!


    – 

  • 1
    @iRon 很高兴知道,谢谢。


    – 


最佳答案
3

要构造一个以相反顺序包含原始哈希表条目的新有序哈希表,您可以枚举原始字典中的键并将位置处的每个新条目插入到新字典中,这实际上会反转原始顺序 – 这是的非管道替代方案,借鉴了该巧妙的技巧:0

# Construct a new ordered hashtable with the entries of the original one in
# reverse order.
$reversedServiceServer = [ordered] @{}
foreach ($key in $serviceServer.Keys) {
  $reversedServiceServer.Insert(0, $key, $serviceServer[$key]) 
}
$reversedServiceServer # Print the new hashtable, for visual verification.

如果以相反的顺序流式传输有序字典的条目就足够了,使用管道

$i = 0; $serviceServer.GetEnumerator() | Sort-Object { (--([ref] $i).Value) }

更新中的基于 的解决方案是一种更高效、更优雅的替代方法(它以相反的顺序返回原始字典条目的堆栈;如果在管道中使用,条目将被自动枚举;要明确将堆栈变成数组请附加)。.ToArray()

  • 虽然比顶部的解决方案更简洁,但由于通常使用管道,Sort-Object特别是使用计算属性(脚本块),它显然更慢(尽管对于小的哈希表来说这并不重要)。

  • Sort-Object调用使用辅助变量,该变量针对每个输入对象减少,因此实际上反转了输入对象的顺序;[1] 是在([ref] $i)范围中引用变量的简洁方式,这是必要的,因为范围中运行$i

  • 虽然输出看起来像一个条目反转的有序哈希表,但它是一个实例,每个实例都有一个.Key和一个.Value属性(PowerShell 另外将其别名.Key.Name)。


[1] 这项技术是从中借鉴而来的。

1

  • 1
    这似乎是实际上最快和最好理解的方式。


    – 

三个想法,一个是使用IndexOf-Descending排序:

$serviceServer = [ordered]@{
    'service3'   = @('server1')
    'service1'   = @('server2', 'server3')
    'service2'   = @('server1', 'server2', 'server3')
    'servicefoo' = @('server1')
}

$keys = @($serviceServer.Keys)
$serviceServer.GetEnumerator() | Sort-Object { $keys.IndexOf($_.Key) } -Descending

第二个是使用

[System.Linq.Enumerable]::Reverse([object[]] $serviceServer.Keys) |
    ForEach-Object { "Service: '$_' - [$($serviceServer[$_])]" }

第三个是通过强制转换为

[System.Collections.Stack] $serviceServer

在所有情况下,原件OrderedDictionary都是完整的,但您可以按照此逻辑创建一个新的。

3

  • 1
    看上去很高效。


    – 

  • 1
    @elh 不认为效率对于这么小的字典那么重要,我添加了第三个选项,在我看来,最简单的一个是使用Stack


    – 

  • 1
    @mklement0 谢谢。我没想到这会有用,但确实有用,太棒了!


    – 

另一个选项是返回反转的OrderedDictionary,以防万一有要求的话:

$reversed = $serviceServer.GetEnumerator() `
    | foreach-object `
        -begin   { $r = [ordered] @{} } `
        -process { $r.Insert(0, $_.Key, $_.Value) } `
        -end     { $r }

它使用 OrderedDictionaryInsert方法遍历原始集合并将每个项目插入新实例的开头,从而有效地将其反转。

1

  • 1
    哦天哪…它变得越来越高效了。


    –