我想生成下表:
AAA BBB CCC
--- --- ---
10 10 10
10 10 10
10 10 10
10 10 10
10 10 10
因此,我使用 foreach
循环编写以下代码来生成列名称:
$property = @('AAA', 'BBB', 'CCC') | foreach {
@{ name = $_; expression = { 10 } }
}
@(1..5) | select -Property $property
但是我收到以下错误,指出名称
不是字符串
:
select : The "name" key has a type, System.Management.Automation.PSObject, that is not valid; expected type is System.String.
At line:4 char:11
+ @(1..5) | select -Property $property
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Select-Object], NotSupportedException
+ FullyQualifiedErrorId : DictionaryKeyIllegalValue2,Microsoft.PowerShell.Commands.SelectObjectCommand
为了使代码正常工作,我必须将 $_
转换为 string
,如下所示:
$property = @('AAA', 'BBB', 'CCC') | foreach {
@{ name = [string]$_; expression = { 10 } }
}
@(1..5) | select -Property $property
或者像下面这样:
$property = @('AAA', 'BBB', 'CCC') | foreach {
@{ name = $_; expression = { 10 } }
}
$property | foreach { $_.name = [string]$_.name }
@(1..5) | select -Property $property
问题是:$_
已经是一个字符串
。为什么我必须再次将其转换为字符串
?为什么 select
认为 name
是 PSObject
?
为了确认它已经是一个字符串
,我编写了以下代码来打印name
的类型:
$property = @('AAA', 'BBB', 'CCC') | foreach {
@{ name = $_; expression = { 10 } }
}
$property | foreach { $_.name.GetType() }
以下结果确认它已经是一个字符串
:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True String System.Object
True True String System.Object
我知道还有许多其他更简单的方法来生成表格。但我想了解为什么我必须将 string
转换为 string
才能使代码正常工作,以及为什么 select
不认为字符串
是一个字符串
。就其值(value)而言,我的 $PSVersionTable.PSVersion
是:
Major Minor Build Revision
----- ----- ----- --------
5 1 18362 1474
最佳答案
您会看到 PowerShell 在幕后使用的偶然的、通常不可见[psobject]
包装器带来的不幸影响。
在您的情况下,由于输入字符串是通过管道提供的,因此它们会被包装并存储为哈希表中的[psobject]
实例,这就是原因的问题。
解决方法 - 既不明显也没有必要 - 是通过访问 .psobject.BaseObject
来丢弃包装器:
$property = 'AAA', 'BBB', 'CCC' | ForEach-Object {
@{ name = $_.psobject.BaseObject; expression = { 10 } }
}
1..5 | select -Property $property
注意:
就您而言,
.psobject.BaseObject
的更简单替代方案(请参阅概念 about_Intrinsic Members 帮助主题)是调用.ToString()
,假设您想要一个字符串。要测试给定值/变量是否存在此类包装器,请使用
-is [psobject]
;使用您的原始代码,以下结果将产生$true
,例如:$property[0].name -is [psobject]
- 但请注意,此测试对于
[pscustomobject]
实例毫无意义,它始终$true
(自定义对象位于Essential[psobject]
实例,没有 .NET 基础对象 - 它们仅具有动态属性)。
通常不可见的 [psobject]
包装器在特定情况下,模糊地导致行为差异,这可以说是一个错误,并且是GitHub issue #5579 。
更简单、更快的替代方案,使用 .ForEach()
array method :
$property = ('AAA', 'BBB', 'CCC').ForEach({
@{ name = $_; expression = { 10 } }
})
1..5 | select -Property $property
与管道不同,.ForEach()
方法不会将 $_
包装在[psobject]
,因此不会出现问题,也不需要解决方法。
使用该方法也更快,但请注意,与管道不同,它必须预先收集内存中的所有输入(显然这不是数组文字的问题)。
关于powershell - select cmdlet 不接受计算属性中生成的字符串名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67843055/