当调用仅返回一个对象时,如何强制Powershell返回数组?


123

我正在使用Powershell在Web服务器上设置IIS绑定,并且以下代码有问题:

$serverIps = gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort

if ($serverIps.length -le 1) {
    Write-Host "You need at least 2 IP addresses for this to work!"
    exit
}

$primaryIp = $serverIps[0]
$secondaryIp = $serverIps[1]

如果服务器上有2个以上的IP,则很好-Powershell返回一个数组,我可以查询该数组的长度并提取第一个和第二个地址。

问题是-如果只有一个IP,Powershell不会返回一个单元素的数组,而是返回IP地址(作为字符串,如“ 192.168.0.100”)-字符串具有.length属性,它大于1,所以测试通过,最后我得到字符串中的前两个字符,而不是集合中的前两个IP地址。

如何强制Powershell返回一个元素集合,或者确定返回的“事物”是否是对象而不是集合?


28
PowerShell的最令人讨厌的一面/臭虫缠身的方面..
user2864740

我认为您的例子过于复杂。更简单的问题:<< $ x = echo Hello; $ x -is [Array] >>得出False。
劳尔·萨利纳斯-蒙塔古多

在Powershell 5中这种行为发生了变化吗?我有一个类似的问题,我无法在5日重现,但可以在4日重现
NickL

Answers:


143

通过以下两种方式之一将变量定义为数组:

将您的管道命令括在括号中,@并以开头:

$serverIps = @(gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort)

将变量的数据类型指定为数组:

[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort

或者,检查变量的数据类型...

IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }

28
包装一个命令@(...)将返回一个数组,即使有零个对象。但是,[Array]如果对象为零,则将结果分配给-typed变量仍将返回$ null。
Nic

1
请注意,如果返回的对象是PSObject(可能是其他对象),则这些解决方案均无效。
致命百吉

2
@ Deadly-Bagel可以举个例子吗?对于我来说,@(...)可以为任何类型的对象正常工作(产生预期会产生的结果)。
user4003407 '16

1
有趣的是,您最终还是遇到了同样的问题。我有一个(又有一个)稍有不同的问题,是的,因为在这个问题中它可以正常工作,但是从函数返回时,情况就不同了。如果有一个元素,则忽略该数组,仅返回该元素。如果在变量前加上逗号,则将其强制为数组,但是多元素数组将返回二维数组。非常乏味。
Deadly-Bagel

1
Gah,这也是上次发生的情况,现在我无法复制。无论如何,我通过使用Return ,$out似乎一直有效的方法解决了我最近的问题。如果再次遇到问题,我将举一个例子。
Deadly-Bagel

13

将结果强制为Array,以便可以拥有Count属性。单个对象(标量)没有Count属性。字符串具有length属性,因此您可能会得到错误的结果,请使用Count属性:

if (@($serverIps).Count -le 1)...

顺便说一句,不是使用也可以匹配字符串的通配符,而是使用-as运算符:

[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" | Select-Object -ExpandProperty IPAddress | Where-Object {($_ -as [ipaddress]).AddressFamily -eq 'InterNetwork'}

为此,他不能只检查数据类型-is吗?
JNK 2012年

字符串具有.length属性-这就是它起作用的原因... :)
Dylan Beattie 2012年

8

如果您提前将变量声明为数组,则可以向其中添加元素-即使它只是一个...

这应该工作...

$serverIps = @()

gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort | ForEach-Object{$serverIps += $_}

实际上,我觉得这是最清晰,最安全的选择。您可以可靠地在集合上使用“ .Count-ge 1”或“ Foreach”
Jaigene Kang

2

您可以Measure-Object用来获取实际的对象计数,而无需求助于对象的Count属性。

$serverIps = gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort

if (($serverIps | Measure).Count -le 1) {
    Write-Host "You need at least 2 IP addresses for this to work!"
    exit
}

1

您可以,在返回列表之前添加逗号(),return ,$list或将其强制转换[Array][YourType[]]在您倾向于使用列表的位置添加。


0

我遇到了将数组传递到Azure部署模板的问题。如果有一个对象,PowerShell会将其“转换”为字符串。在下面的示例中,$a从一个函数返回,该函数根据标签的值获取VM作为对象。我通过将$aNew-AzureRmResourceGroupDeployment包装到cmdlet@()。像这样:

$TemplateParameterObject=@{
     VMObject=@($a)
}

New-AzureRmResourceGroupDeployment -ResourceGroupName $RG -Name "TestVmByRole" -Mode Incremental -DeploymentDebugLogLevel All -TemplateFile $templatePath -TemplateParameterObject $TemplateParameterObject -verbose

VMObject 是模板的参数之一。

可能不是最技术/最强大的方法,但是对于Azure来说就足够了。


更新资料

上面的工作很好。我已经尝试了以上所有内容,但我设法通过$vmObject数组作为与部署模板兼容的唯一方法是,其中一个元素如下(我希望MS再次播放(这是一份报告,已修复错误在2015年)):

[void][System.Reflection.Assembly]::LoadWithPartialName("System.Web.Extensions")
    
    foreach($vmObject in $vmObjects)
    {
        #$vmTemplateObject = $vmObject 
        $asJson = (ConvertTo-Json -InputObject $vmObject -Depth 10 -Verbose) #-replace '\s',''
        $DeserializedJson = (New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer -Property @{MaxJsonLength=67108864}).DeserializeObject($asJson)
    }

$vmObjects 是Get-AzureRmVM的输出。

我传递$DeserializedJson给部署模板的参数(数组类型)。

供参考,可爱的错误New-AzureRmResourceGroupDeployment抛出是

"The template output '{output_name}' is not valid: The language expression property 'Microsoft.WindowsAzure.ResourceStack.Frontdoor.Expression.Expressions.JTokenExpression' 
can't be evaluated.."

0

作为参考对象返回,因此传递时永远不会转换。

return @{ Value = @("single data") }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.