为什么PowerShell会在测试长度上将字符串静默转换为对象[]?

时间:2014-02-18 16:44:00

标签: powershell

Trustedhosts在超过1023个字符的字符串上呕吐,我刚刚破坏了这个限制。我编写了一个很好的小FILO队列,以确保我可以在需要时总是添加一个新的主机,当我发现这个奇怪的时候只省略我最少使用的服务器。我在失去所有头发之前治好了,所以这很好。我不明白的是它为什么会发生。

$namesString = 'server1.there.com,server2.there.com,server3.there.com'
Write-Host ("So, we have a nice little list of servers. Let's trust them.")
Set-Item wsman:localhost\client\trustedhosts -Value $namesString -Force 
Write-Host ("They're nicely trusted now")
if ($namesString.length -gt 1) {
    Write-Host ("Now we know it's longish")
}
Write-Host ("OK. Let's try trusting the same string.")
Write-Host ("(By the way, it's a $($namesString.getType()))")
Write-Host ("(and let's check whether it's an array: $($namesString -is [array]))")
Set-Item wsman:localhost\client\trustedhosts -Value $namesString -Force 
Write-Host ("Why did testing the length of the string cause this confirmed and tested string to report as an object array?")

以下输出结果

So, we have a nice little list of servers. Let's trust them.
They're nicely trusted now
Now we know it's longish
OK. Let's try trusting the same string.
(By the way, it's a string)
(and let's check whether it's an array: False)
Set-Item : Cannot convert 'System.Object[]' to the type 'System.String' required by the parameter. Specified method is not sup
ported.
At C:\Users\...\AppData\Local\Temp\2\07da58ce-2578-4603-8291-83e1cc231522.ps1:11 char:9
+ Set-Item <<<<  wsman:localhost\client\trustedhosts -Value $namesString -Force 
    + CategoryInfo          : NotSpecified: (:) [Set-Item], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException,Microsoft.PowerShell.Commands.SetItemCommand
Why did testing the length of the string cause this confirmed and tested string to report as an object array?

这种奇怪的东西埋藏在2000行代码中,并且有问题的字符串经历了3次转换。在每个回合中,字符串都将自己报告为字符串,但是wsman将其作为数组拒绝。它花了一些严肃的二进制搜索才最终弄清楚它是长度测试静默变成某种加密数组。

2 个答案:

答案 0 :(得分:5)

在PowerShell V1和V2中,当您获取或设置属性或调用成员时,PowerShell会将值包装在PSObject中,以便它可以使用它的内部类型适配器来执行操作。

创建这个PSObject有点贵,所以如果你的对象是一个简单的变量引用,那么PSObject就存储在变量中,这样后续操作就不会为创建另一个PSObject的开销付出代价。

如果不清楚,这里有一些例子:

$value = @{ Property = 1 }    # $value is a hashtable

$value.Property    # $value is now a PSObject wrapping the hashtable

$value.Property    # don't need to create another PSObject

# Under the covers, PowerShell V2 creates a PSObject wrapping the
# string before getting the length, but that PSObject isn't saved anywhere
"hello".Length

PowerShell V3进行了重大改进以提高性能,不再需要这种奇怪的副作用来获得良好的性能。

答案 1 :(得分:2)

我尝试使用PowerShell v4并且它在那里正常工作,所以似乎导致此行为的问题已得到解决。

但是,在PowerShell v2中,我得到了与您相同的错误。

我不知道为什么在观察Length属性时对象会被包裹。但是,我找到了一个解决方案。

如果获得PowerShell PSObject对象的string,然后获得该psobject的BaseObject属性;这将是一个string的字符串(而不是包装的字符串)。这将适用于第一种情况下它是string的情况(通过强制换行然后解包)以及它被包装的情况。因此,您可以将设置调用更改为:

set-item WSMAN:\localhost\client\TrustedHosts $str.psobject.BaseObject -Force