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 有两个区别,与您的问题相关:
对象 return 由 return
语句编辑,在 V2:
中包装到 PSObject
[Type]::GetTypeArray(@(&{return "Name"}))[0].FullName
return System.Management.Automation.PSObject
在 V2 和 System.String
在 V4.
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>
我正在努力解决 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 有两个区别,与您的问题相关:
对象 return 由
中包装到return
语句编辑,在 V2:PSObject
[Type]::GetTypeArray(@(&{return "Name"}))[0].FullName
return
System.Management.Automation.PSObject
在 V2 和System.String
在 V4.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>