Powershell多维数组排序

时间:2012-06-26 09:45:04

标签: arrays sorting powershell multidimensional-array

我有一个处理所有SQL Server的脚本。该脚本有几个函数,我有一个错误例程,它将服务器名称,函数名称和错误消息记录到500行x 3列数组中。在脚本结束时,我想将此数组排序为服务器名称序列。有几篇文章建议我需要做的就是将数组传递给sort-object cmdlet,但是当我这样做时,我的数组的每个元素都被system.Object[]替换。 NB。在函数之前填充的数组只是我的数组的一个例子

$global:ErrorCount = 0
$global:ErrArray = new-object 'object[,]' 500,3

$global:ErrArray[1,00]= "SV000004"
$global:ErrArray[1,01]= "ProcessServers"
$global:ErrArray[1,02]= "The server was not found or was not accessible."

$global:ErrArray[2,00]= "BOSWEB02"
$global:ErrArray[2,01]= "GetDatabases"
$global:ErrArray[2,02]= "Database Status = Shutdown"

$global:ErrArray[3,00]= "SATURN"
$global:ErrArray[3,01]= "GetDatabases"
$global:ErrArray[3,02]= "Database Status = Shutdown"

$global:ErrArray[4,00]= "BOSWEB02"
$global:ErrArray[4,01]= "GetSystemInfo"
$global:ErrArray[4,02]= "Access is denied"

$global:ErrorCount = 4

Function DisplayErrors
{
    Write-Host "`nBefore:-`n"

    for ( $iLoop=1; $iLoop -le $global:ErrorCount; $iLoop++)
    {
        "{0,-14}  {1,-18}  {2,-80}" -f 
          $global:ErrArray[$iLoop,0], $global:ErrArray[$iLoop,1], 
          $global:ErrArray[$iLoop,2]
    }

    $Sorted = $global:ErrArray | Sort-Object @{Expression={$_[0]}}

    Write-Host "`nAfter:-`n"    

    for ( $iLoop=1; $iLoop -le $global:ErrorCount; $iLoop++)
    {
        "{0,-14}  {1,-18}  {2,-80}" -f 
          $Sorted[$iLoop,0], $Sorted[$iLoop,1], $Sorted[$iLoop,2]
    }
}  

DisplayErrors

输出如下: -

在: -

SV000004        ProcessServers      The server was not found or was not accessible.                                 
BOSWEB02        GetDatabases        Database Status = Shutdown                                                      
SATURN          GetDatabases        Database Status = Shutdown                                                      
BOSWEB02        GetSystemInfo       Access is denied                                                                

在: -

System.Object[]  System.Object[]     System.Object[]                                                                 
System.Object[]  System.Object[]     System.Object[]                                                                 
System.Object[]  System.Object[]     System.Object[]                                                                 
System.Object[]  System.Object[]     System.Object[]  

有谁能告诉我这里我做错了什么?

非常感谢: - )

3 个答案:

答案 0 :(得分:1)

以这种方式创建数组(锯齿状数组:array [] [])sort-object可以正常工作:

$global:ErrArray += ,@("SV000004","ProcessServers","The server was not found or was not accessible.")
$global:ErrArray += ,@("BOSWEB02","GetDatabases","Database Status = Shutdown")
$global:ErrArray += ,@("SATURN","GetDatabases","Database Status = Shutdown")
$global:ErrArray += ,@("BOSWEB02","GetSystemInfo","Access is denied" )

Function DisplayErrors
{
    Write-Host "`nBefore:-`n"

    foreach ( $server in $global:Errarray)
    {
    write-host $server 
    }

    $sorted = $global:ErrArray | Sort-Object @{Expression={$_[0]}}

    Write-Host "`nAfter:-`n"    

   foreach ( $server in $sorted)
    {
        write-host $server    
    }
}  

DisplayErrors

或更像您的代码:

$global:ErrArray += ,@("SV000004","ProcessServers","The server was not found or was not accessible.")
$global:ErrArray += ,@("BOSWEB02","GetDatabases","Database Status = Shutdown")
$global:ErrArray += ,@("SATURN","GetDatabases","Database Status = Shutdown")
$global:ErrArray += ,@("BOSWEB02","GetSystemInfo","Access is denied" )



Function DisplayErrors
{
    Write-Host "`nBefore:-`n"

for ( $iLoop=0; $iLoop -lt $global:errarray.count; $iLoop++)
{
    "{0,-14}  {1,-18}  {2,-80}" -f   $global:ErrArray[$iLoop][0], $global:ErrArray[$iLoop][1], $global:ErrArray[$iLoop][2]
}

$sorted = $global:ErrArray | Sort-Object @{Expression={$_[0]}}

Write-Host "`nAfter:-`n"    

for ( $iLoop=0; $iLoop -lt $sorted.count; $iLoop++)
    {
        "{0,-14}  {1,-18}  {2,-80}" -f   $Sorted[$iLoop][0], $Sorted[$iLoop][1], $Sorted[$iLoop][2]
    }
}  

DisplayErrors

答案 1 :(得分:0)

Whlist我不知道为什么Powershell会破坏阵列,我可能会有更多的Powershellish解决方案。让我们创建一个包含数据和一维数组的自定义对象。排序很简单。

# Custom error object
function CreateErrorObject {
    param ([string]$server, [string]$proc, [string]$message)
    $objError = New-Object System.Object
    $objError | Add-Member -type NoteProperty -name Server -value $server
    $objError | Add-Member -type NoteProperty -name Proc -value $proc
    $objError | Add-Member -type NoteProperty -name Message -value $message
    $objError
}

$errors = @() # Empty array

# Populate error objects some way
$errors += CreateErrorObject "SV000004" "ProcessServers" "The server was not found or was not accessible"
$errors += CreateErrorObject "BOSWEB02" "GetDatabases" "Database Status = Shutdown"
$errors += CreateErrorObject "SATURN" "GetDatabases" "Database Status = Shutdown"
$errors += CreateErrorObject "BOSWEB02" "GetSystemInfo" "Access is denied"


$errors # Unsorted list
$errors | sort -Property server # sort by server property
$errors | sort -Property proc # sort by proc property
$errors | sort -Property message # sort by message property

答案 2 :(得分:0)

只是代码,女士

警告:Quicksorts do exist

我在实现中将ErrArray更改为0 -

  

$ global:ErrArray [ 0 ,00] =“SV000004”

...但是否则插入以下功能,交易线......

$Sorted = $global:ErrArray | Sort-Object @{Expression={$_[0]}}

... for ...

$Sorted = Order-2dArray $global:ErrArray;

# Naming sorts stinks: http://stackoverflow.com/questions/27173621/
# NOTE: **EVERY** array entry in [0,x] must be non-null if -ColCount not set.
# NOTE: **EVERY** array entry in [y,0] must be non-null. Thanks.
Function Order-2dArray
{
    param
    (
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
        $ArrayToSort,
        [int]$ColCount = -1,  # I hate multi-dim PS arrays.
        [int]$SortColumn = 0, # Would be a touch faster if this were a constant.
        [switch][alias("desc")]$Descending # Ascend by default.
    )

    $fullCount = $ArrayToSort.Length;
    if ($ColCount -le 0) # if not given, guess by checking for $nulls.
    {
        $ColCount=0;while ($ArrayToSort[0,$ColCount] -ne $null) {
            $ColCount++;
        }
    }

    $RowCount = $fullCount / $ColCount;
    $nullRowCap = $RowCount-1;  # 1-based to 0-based
    while ($ArrayToSort[$nullRowCap,0] -eq $null) { $nullRowCap--; }

    $itemToPlace = $nullRowCap;
    for ($i=0;$i -lt $nullRowCap;$i++)
    {
        for ($j=0;$j -lt $itemToPlace;$j++)
        {
            # This dual-check method for optionally descending is not efficient.
            if (($ArrayToSort[$j,$SortColumn] -gt $ArrayToSort[($j+1),$SortColumn] -and !$Descending) -or
                ($ArrayToSort[$j,$SortColumn] -lt $ArrayToSort[($j+1),$SortColumn] -and $Descending))
            {
                for($k=0;$k -lt $ColCount;$k++) {
                    $hold = $ArrayToSort[$j,$k];
                    $ArrayToSort[$j,$k] = $ArrayToSort[($j+1),$k];
                    $ArrayToSort[($j+1),$k] = $hold;
                }
            }
        }
        $itemToPlace--;
    }
    Write-Host -BackgroundColor Magenta $ArrayToSort.GetType();
    , $ArrayToSort; # see http://stackoverflow.com/questions/7833317/ for comma use
}

为什么原来不起作用?

[意识到这是一个僵尸问题 - ]这对于评论来说有点太大了,所以我将创建一个答案来解释为什么你需要使用@CB。或@vonPryz建议。

首先,请看结尾处的评论。在PowerShell 1.0中,OP的方法可以起作用。显然不是现在。

简而言之,您无法再访问PowerShell中的“二维数组第一行中的值”。如果要将条目的每个“行”分组到对象中并将该数组传递给Sort-Object,则需要有对象数组。因此,您可以选择数组,如CB。的答案,或 New-Object个数组,就像在vonPryz中一样 - 或者其他任何感觉最好。但它必须是X 的数组,而不是二维数组。

要显示内容,请尝试

PS> $global:ErrArray[0]

......在任何时候。你会得到......

You cannot index into a 2 dimensional array with index [0].
At line:1 char:1
+ $global:ErrArray[0]
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NeedMultidimensionalIndex

也就是说,没有$ global:ErrArray [0]返回三个值,至少不是你想象的那样。数组的第一个维度不会对第二个维度中的三个对象进行分组。 它更像1500个地址而不是500个楼层,每个地址有三个地址。


好的,那么为什么代码不会出错?

在您的代码中尝试此操作以确认它为什么不会抛出错误:

$Sorted = $global:ErrArray | Sort-Object @{
    Expression={
        $_[0];
        Write-Host $_[0];
    }
}

结果可能会让你感到惊讶 - 这是每个字符串的第一个字符。

S
P
T
B
G
D
S
G
D
B
G
A

由于没有“多维数组的单行”概念,PowerShell会将二维数组中的所有条目解压缩为字符串的单维数组,并使用{ {1}},你正在抓取字符串中的第一个字符作为数组。 ! (如果你在每个点上都有整数而不是字符串,我相信这些代码会在这里打破。)

这意味着您不只是从二维数组中的任何位置对所有非空值进行排序,而只是按每个位置的第一个字母排序!

$_[0]之所以有效,是因为(并且我同意这非常违反直觉)PowerShell会在现在的单维$Sorted[$iLoop,0]中为您提供$iLoop - 和0个值阵列。它认为你为新的扁平数组传递两个索引,返回的值是一个包含这两个值的数组。

尝试使用这两行代替$Sorted一行:

-f

第一个“有效” - 它为"{0} {1}" -f $Sorted[$iLoop,0] "{0} {1}" -f $Sorted[$iLoop,0], $Sorted[$iLoop,1], $Sorted[$iLoop,2] 提供了$iLoop $Sorted的{​​{1}}和{0}的第0个值,因为您基本上传递了两个字符串。它们可能已作为数组传入,但PowerShell“帮助”将其解压缩为两个字符串。完美,有点。

第二行返回到你看到的{1},因为它是......一个由三个数组组成的数组(因为它认为你使用的是数组逗号表示法:System.Object[]),每个长度为2(从它认为从X,Y,Z看到的两个索引,等等)。在这种情况下,数组中的前两个元素被推入$iLoop, 0{0},隐式调用{1},并变为ToString()

当我们有像System.Object[]之类的东西时,我们确认它正在返回数组。在这里,我正在使用PowerGUI并在排序之后删除一个断点来调试:

$Sorted[$iLoop,0]

因此,您要向格式调用中发送三个[DBG]: PS> $t2 = $Sorted[$iLoop,0], $Sorted[$iLoop,1], $Sorted[$iLoop,2] [DBG]: PS> $t2.Length 3 [DBG]: PS> $t2[0] BOSWEB02 BOSWEB02 [DBG]: PS> $t2[0][0] BOSWEB02 数组,其中包含两个字符串。在Object[]上调用ToString()可以获得令人讨厌的System.Object []。

如果你想按照每个第一维第二维的第0个元素中的内容对二维数组进行排序[原文如此],你可能不得不把它写成旧式,泡泡或者Quicksort或者什么 - 有你,就像我在这个答案的第一部分所做的那样。 (如果可以的话,最好使用CB。建议,我认为。)

所以这一切都有道理。对于PowerShell,您有1500个地址,但没有按“Object[]”值对它们进行实际分组。别担心,它对我来说也没有立竿见影(因此我对这个问题感到磕磕绊绊)。

编辑:为了让事情更有趣,看来数组 正如您在PowerShell v1.0中所期望的那样工作:http://arstechnica.com/civis/viewtopic.php?t=52603但是看看脚本是如何工作的Guy在2011年已经doing arrays of arrays for multiple dimensions