我想生成下表:
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
但我得到以下错误,说name
不是string
:
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
问题是:$_
已经是string
了。为什么我必须再次将其转换为string
?为什么select
认为name
就是PSObject
?
为了确认它已经是string
,我写了以下代码来打印name
的类型:
$property = @('AAA', 'BBB', 'CCC') | foreach {
@{ name = $_; expression = { 10 } }
}
$property | foreach { $_.name.GetType() }
以下结果确认它已经是string
:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
True True String System.Object
True True String System.Object
我知道还有很多其他更简单的方法可以生成表格。但我想了解为什么我必须将string
转换为string
才能使代码工作,以及为什么select
不认为string
是string
。就其价值而言,我的$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_Itrisic Members帮助主题(是调用.ToString()
,前提是您需要一个字符串。要测试给定值/变量是否存在这样的包装,请使用
-is [psobject]
;对于原始代码,以下生成$true
,例如:$property[0].name -is [psobject]
- 但是,请注意,此测试对于
[pscustomobject]
实例没有意义,因为它总是$true
(自定义对象本质上是没有.NET基对象的[psobject]
实例,它们只有动态属性(
通常不可见的[psobject]
包装器在特定情况下、模糊地导致行为差异,这可以说是一个错误,也是GitHub第5579期的主题。
使用.ForEach()
阵列方法的更简单、更快的替代方案:
$property = ('AAA', 'BBB', 'CCC').ForEach({
@{ name = $_; expression = { 10 } }
})
1..5 | select -Property $property
与管道不同,.ForEach()
方法不会将$_
封装在[psobject]
中,因此不会出现问题,也不需要解决方法。
使用该方法也更快,不过请注意,与管道不同,它必须提前在内存中收集所有输入(显然不是数组文字的问题(。