2012-06-19 14 views
62

estoy usando Powershell para establecer enlaces de IIS en un servidor web, y que tiene un problema con el siguiente código:¿Cómo puedo forzar a Powershell a devolver una matriz cuando una llamada solo devuelve un objeto?

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

Si hay 2+ IP en el servidor, bien - Powershell devuelve una matriz, y Puedo consultar la longitud de la matriz y extraer la primera y la segunda direcciones.

El problema es que, si solo hay una IP, Powershell no devuelve una matriz de un elemento, devuelve la dirección IP (como una cadena, como "192.168.0.100") - la cadena tiene una propiedad .length, es mayor que 1, por lo que la prueba pasa, y termino con los dos primeros caracteres en la cadena, en lugar de las dos primeras direcciones IP de la colección.

¿Cómo puedo forzar a Powershell a devolver una colección de un elemento, o bien determinar si la "cosa" devuelta es un objeto en lugar de una colección?

+0

aspecto más sin ayuda molesto/plagada de errores de PowerShell .. – user2864740

Respuesta

79

Definir la variable como una matriz en una de dos maneras ...

Envuelva sus comandos por tubería entre paréntesis con un @ al principio:

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

Especificar el tipo de datos de la variable como una matriz:

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

O, comprobar el tipo de datos de la variable ...

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

Envolver un comando en' @ (...) 'devolverá una matriz incluso si hay cero objetos. Mientras que asignar el resultado a una variable de tipo '[Array]' aún devolverá $ null si hay cero objetos. – Nic

+0

Solo una nota que ninguna de estas soluciones funciona si el objeto que se devuelve es un objeto PSObject (posiblemente otros). –

+1

@ Deadly-Bagel ¿Puedes mostrar un ejemplo de esto? Para mí '@ (...)' funciona correctamente (produce el resultado que espero que produzca) para cualquier tipo de objeto. – PetSerAl

9

Fuerce el resultado a una matriz para que pueda tener una propiedad Count. Los objetos individuales (escalares) no tienen una propiedad de Cuenta. Cuerdas tienen una propiedad de longitud lo que es posible obtener resultados falsos, utilizan la propiedad Count:

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

Por cierto, en lugar de utilizar un comodín que también puede coincidir con cadenas, utilice el operador -as:

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

Para que esto podría no que también acaba de comprobar el tipo de datos con '-is ¿? – JNK

+0

Las cadenas tienen una propiedad .length; por eso está funcionando ... :) –

+0

Correcto, lo solucionará enseguida. –

5

Si se declara la variable como una matriz antes de tiempo, puede agregarle elementos, incluso si es solo uno ...

Esto debería funcionar ...

$serverIps = @() 

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

De hecho, siento que esta es la opción más clara y segura. Puede usar de manera confiable ".Count - ge 1 'en la colección o' Foreach ' –

0

Tuve este problema al pasar una matriz a una plantilla de implementación de Azure. Si había un objeto, PowerShell lo "convirtió" en una cadena. En el ejemplo siguiente, $a se devuelve de una función que objeta VM en función del valor de una etiqueta. Paso el $a al cmdlet New-AzureRmResourceGroupDeployment envolviéndolo en @(). De este modo:

[email protected]{ 
    [email protected]($a) 
} 

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

VMObject es uno de los parámetros de la plantilla.

Puede que no sea la forma más técnica/robusta de hacerlo, pero es suficiente para Azure.


actualización

bien lo anterior ha funcionado. He intentado todo lo anterior y algunos, pero la única forma en que logré pasar $vmObject como una matriz, compatible con la plantilla de implementación, con un elemento es la siguiente (supongo que MS ha estado jugando de nuevo (esto era un informe y Corregido un fallo en el año 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 es el resultado de Get-AzureRmVM.

Paso $DeserializedJson al parámetro 'plantilla de despliegue' (de tipo matriz).

Como referencia, la hermosa error New-AzureRmResourceGroupDeployment tiros es

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