Powershell 隐式转换数组以防止在 v2 中使用 Select -属性

Powershell implicitly converting arrays preventing use with Select -Property in v2

我正在努力解决 powershell 隐式修改 powershell 2 独占 中的数据结构的问题。同样的逻辑在 powershell 4 中运行良好。

我创建了一个辅助函数,它给定了一个字段,一个别名 and/or 一个块表达式,然后我可以将其提供给第二个函数,该函数构建一个 name/expression 数组,该数组可以传递给Select -属性 请求。这在 powershell 4 中运行良好,但是我在混合环境中工作,当我尝试在 powershell 2 服务器上使用它时,脚本失败 return 出现数据类型转换错误。

Cannot convert System.Management.Automation.PSObject to one of the following types {System.String, System.Management.Automation.ScriptBlock}.
+ CategoryInfo          : InvalidArgument: (:) [Select-Object], NotSupportedException
+ FullyQualifiedErrorId : DictionaryKeyUnknownType,Microsoft.PowerShell.Commands.SelectObjectCo    mmand
+ PSComputerName        : ####

我现在基本上必须在调用函数中重建数组,因为即使显式转换为数组也会引发相同的异常。

if ($PSVersionTable["PSVersion"].Major -eq 2)
{
    #write-host "rebuilding setting"
    $origprops = [System.Array](Get-SelectPropertyArray2 $propsToSelect)
    $props = @()
    for ($i =0 ; $i -lt $origprops.Count ; $i++)
    {
        #write-host $i
        $props += @{n=([System.Array]$origprops)[$i]["n"];e=(([System.Array]$origprops)[$i]["e"])}
    }
}

以下不足:

[System.Array](Get-SelectPropertyArray2 $propsToSelect)

当我在初始化时执行 GetType() 时,除了数组和 return 语句外,类型总是 System.Object、System.Array

然而在调用函数中它变成了System.HashTable

下面是一个使用函数的例子:

$propsToSelect = @("Name","Path","PSPath")
# Get-SelectPropertyArraySet takes a field to return, an alias and an optional code block (to override the default)
$propsToSelect += Get-SelectPropertyArraySet "." "Asp_AppAllowClientDebug" {
                    (get-webconfigurationProperty -filter /system.webServer/asp -Name AppAllowClientDebug -PSPath $PSPath).Value }
$propsToSelect += Get-SelectPropertyArraySet "." "Asp_AppAllowDebugging" {
                    (get-webconfigurationProperty -filter /system.webServer/asp -Name AppAllowDebugging -PSPath $PSPath).Value }

$props = (Get-SelectPropertyArray2 $propsToSelect)
# the following line is the one that fails
$config = Get-Item $PSPath | select -Property $props

下一行是失败的行除非我重建数组如上所示。

$config = Get-Item $PSPath | select -Property $props

当我写出类型时,我得到以下结果:

PS C:\scripts\ps\bits> $p = Get-SelectPropertyArray2 $set
init:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


Inc Alias:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


return:

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


PS C:\scripts\ps\bits> $p.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Hashtable                                System.Object

如您所见,最后一个完全不同,如果我随后在我的 select 语句中使用它,它将失败并出现之前显示的异常。

函数代码如下:

<#
    .SYNOPSIS
    Generates a PSObject with a Field/Alias/Expression set for use with the Get-SelectPropertyArray2 function.
    .DESCRIPTION
    Generates a PSObject with a Field/Alias/Expression Set for use with the Get-SelectPropertyArray2 function.

    The Alias property is optional

    .EXAMPLE
    Get-SelectPropertyArraySet "Name"

    .EXAMPLE 
    Get-SelectPropertyArraySet "AccessFlags" "Access Flags"

    .EXAMPLE 
    Get-SelectPropertyArraySet "AccessFlags" "Access Flags"

    .EXAMPLE
    $propsToSelect = @(
                        (Get-SelectPropertyArraySet "Name"),
                        (Get-SelectPropertyArraySet "AccessFlags" "Access Flags"),
                        (Get-SelectPropertyArraySet "AuthAnonymous" "Auth Anonymous")
                        )

    Get-SelectPropertyArray2  $propsToSelect

    .PARAMETER Field
    [string] Name of the field to select
    .PARAMETER Alias
    [string] Alias Name of the field to select
    .PARAMETER Expression
    [ScriptBlock] Script block to use for capturing the data, allows for custom field definitions outside standard aliasing.

#>
function Get-SelectPropertyArraySet 
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [Alias("f")]
           [string]$Field,
        [Parameter(Mandatory=$false)]
        [Alias("a")]
           [string]$Alias,
        [Parameter(Mandatory=$false)]
        [Alias("e")]
           [ScriptBlock]$Expression
    )
    #Write-Host "Building Property Set for $field, $alias"# with $Expression"
    new-object PSObject -Property @{Field=$field; Alias=$alias; Expression=$expression}
}

第二个函数(包括写出数据类型的行,但被注释掉了):

<#
    .SYNOPSIS
    Generates an array of script blocks which can be passed to a select statement. Aliasing the field name.
    .DESCRIPTION
    Generates an array of script blocks which can be passed to a select statement. Aliasing the field name.

    The input is an PSObject with a Field and Alias property

    For Example:
    Field:    Alias:       
    -----------  ------------
    Name         Name
    MyField      MyField Alias

    A helper function Get-SelectPropertyArraySet can be used to create this PSObject.

    .EXAMPLE
    $propsToSelect = @(
                        (Get-SelectPropertyArraySet "Name"),
                        (Get-SelectPropertyArraySet "AccessFlags" "Access Flags"),
                        (Get-SelectPropertyArraySet "AuthAnonymous" "Auth Anonymous")
                        )
    $propsToSelect | Get-SelectPropertyArray2

    .EXAMPLE
    $propsToSelect = @(
                        (Get-SelectPropertyArraySet "Name"),
                        (Get-SelectPropertyArraySet "AccessFlags" "Access Flags"),
                        (Get-SelectPropertyArraySet "AuthAnonymous" "Auth Anonymous")
                        )

    Get-SelectPropertyArray2  $propsToSelect
    .EXAMPLE
    $propsToSelect = @(
                        (new-object PSObject -Property @{Field="Name"}),
                        (new-object PSObject -Property @{Field="AccessFlags"; Alias="Access Flags"}),
                        (new-object PSObject -Property @{Field="AuthAnonymous"; Alias="Auth Anonymous"})
                        )

    Get-SelectPropertyArray2  $propsToSelect




    .PARAMETER PropertiesToSelect
    [PSObject] array containing the Field/Alias pair to use

#>
function Get-SelectPropertyArray2
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [Alias("Prop")]
           [PSObject]$PropertiesToSelect
    )
    begin {
        #Initialize property-array for Select-Object
        $PropArray = @()
        #write-host "init"
        #write-host init: 
        #$PropArray.GetType() | out-host

    }
    process {

        #create script block for each field in the input array/pipeline
        #write-host "start"
        #$PropertiesToSelect | select Field,Alias
        foreach ($PropertySet in $PropertiesToSelect)
        {
            $sb = $null
            if ($PropertySet.GetType().Name -eq "String")
            {
                #Write-Host string found
                $fieldName = $PropertySet
                $fieldAlias = $null
            }
            else
            {
                #Write-Host object found
                $fieldName = $PropertySet.Field
                $fieldAlias = $PropertySet.Alias  
                #use provided expression if present and valid type
                if ($PropertySet.Expression)
                {
                    #write-host "Expression Found"
                    #$Propertyset.Expression.GetType().Name
                    switch ($Propertyset.Expression.GetType().Name)
                    {
                        "ScriptBlock"
                        {
                            #Write-Host "Expression is [ScriptBlock]"
                            $sb = $PropertySet.Expression
                        }
                        "String"
                        {
                            #Write-Host "Expression is [String], converting to [ScriptBlock]"

                            $sb = [Scriptblock]::Create($PropertySet.Expression)
                        }
                    }
                }
            }



            if (! ($sb)) { <#write-host "No SB, using default";#> $sb = [Scriptblock]::Create("`$_.`"$fieldName`"") }

            #"Mapping Field: {0} --> {1}" -f $fieldName,$FieldAlias |Write-Host
            if ($FieldAlias)
            {
                #write-host Inc Alias: 
                #$PropArray.GetType() | out-host
                $PropArray += @{n=[string]$fieldAlias;e=($sb)}
            }
            else #singleton - no alias
            {
                #write-host Inc Field: 
                #$PropArray.GetType()  |out-host
                $PropArray += @{n=[string]$fieldName;e=($sb)}
            }
        }

        # return the script block array
        #Write-Host "end"
        #write-host "<--$PropertiesToSelect"


    }
    end {
        #write-host "return:"
        #write-host return: 
        #$PropArray.GetType() |out-host



        return $PropArray
    }

}

V2 与 V4 有两个区别,与您的问题相关:

  1. 对象 return 由 return 语句编辑,在 V2:

    中包装到 PSObject
    [Type]::GetTypeArray(@(&{return "Name"}))[0].FullName
    

    return System.Management.Automation.PSObject 在 V2 和 System.String 在 V4.

  2. Select-Object V2 中的 cmdlet 无法处理 Property 参数的包装对象:

    Select-Object (,[PSObject]"Name")
    

    V2 错误,V4 正常。

PS:
我可以问一件事吗?你这样做有什么好处:

$propsToSelect = @(
    Get-SelectPropertyArraySet "Name"
    Get-SelectPropertyArraySet "AccessFlags" "Access Flags"
    Get-SelectPropertyArraySet "AuthAnonymous" "Auth Anonymous"
)
$props = Get-SelectPropertyArray2 $propsToSelect

或者这个:

$propsToSelect = @(
    new-object PSObject -Property @{Field="Name"}
    new-object PSObject -Property @{Field="AccessFlags"; Alias="Access Flags"}
    new-object PSObject -Property @{Field="AuthAnonymous"; Alias="Auth Anonymous"}
)
$props = Get-SelectPropertyArray2 $propsToSelect

关于这个:

$props = @(
    "Name"
    @{Expression="AccessFlags"; Name="Access Flags"}
    @{Expression="AuthAnonymous"; Name="Auth Anonymous"}
)

?

一般解决方法:

<em><对象></em> | Select-Object -属性 <strong>([String[]]</strong>(<em><array></em>)<strong>) </strong>

示例:

$config = 获取项目 $PSPath | Select -属性 <strong>( [String[]]</strong>$props <strong>)</strong>