160

我正在使用 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 返回单元素集合,或者确定返回的“事物”是否是对象而不是集合?

4

7 回答 7

182

以两种方式之一将变量定义为数组...

将您的管道命令包装在括号中,@并在开头使用 a:

$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> }
于 2012-06-19T18:52:10.070 回答
15

将结果强制为 Array,以便您可以拥有 Count 属性。单个对象(标量)没有 Count 属性。字符串具有长度属性,因此您可能会得到错误的结果,请使用 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'}
于 2012-06-19T18:51:58.540 回答
9

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

这应该工作...

$serverIps = @()

gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort | ForEach-Object{$serverIps += $_}
于 2012-06-19T18:53:24.123 回答
8

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

于 2019-07-10T19:05:28.890 回答
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
}
于 2018-07-10T12:27:52.703 回答
1

作为引用对象返回,因此它在传递时永远不会转换。

return @{ Value = @("single data") }
于 2020-02-21T00:24:32.070 回答
0

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

$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.."
于 2017-11-29T20:38:34.573 回答