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

我正在使用 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,fine-Powershell 返回一个数组,我可以查询数组长度并提取第一个和第二个地址。

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

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

77252 次浏览

强制结果为 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'}

用两种方法之一将变量定义为数组..。

在括号中用开头的 @包装您的管道命令:

$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> }

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

这个应该可以..。

$serverIps = @()


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

我在将数组传递到 Azure 部署模板时遇到了这个问题。如果有一个对象,PowerShell 将其“转换”为字符串。在下面的示例中,$a从一个函数返回,该函数根据标记的值使 VM 被拒绝。我通过将 $a包装在 @()中将其传递给 New-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年修复了 bug)) :

[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传递给部署模板的参数(类型为 array)。

作为参考,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.."

您可以使用 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
}

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

作为引用对象返回,所以它在传递。

return @{ Value = @("single data") }

有个办法可以解决你的问题。让大多数代码保持原样,只是改变处理 $serverIps对象的方式。这段代码可以处理 $null、一个项目和多个项目。

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


# Always use ".Count" instead of ".Length".
# This works on $null, only one item, or many items.
if ($serverIps.Count -le 1) {
Write-Host "You need at least 2 IP addresses for this to work!"
exit
}


# Always use foreach on a array-possible object, so that
# you don't have deal with this issue anymore.
$serverIps | foreach {
# The $serverIps could be $null. Even $null can loop once.
# So we need to skip the $null condition.
if ($_ -ne $null) {
# Get the index of the array.
# The @($serverIps) make sure it must be an array.
$idx = @($serverIps).IndexOf($item)


if ($idx -eq 0) { $primaryIp = $_ }
if ($idx -eq 1) { $secondaryIp = $_ }
}
}

在 PowerShell Core 中,每个对象都有一个 .Count属性。在 Windows PowerShell 中,“几乎”每个对象都有一个 .Count属性。