Wie kann ich Powershell zwingen, ein Array zurückzugeben, wenn ein Aufruf nur ein Objekt zurückgibt?

123

Ich verwende Powershell, um IIS-Bindungen auf einem Webserver einzurichten, und habe ein Problem mit dem folgenden Code:

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

Wenn auf dem Server mehr als 2 IPs vorhanden sind, ist dies in Ordnung. Powershell gibt ein Array zurück. Ich kann die Länge des Arrays abfragen und die erste und die zweite Adresse in Ordnung extrahieren.

Das Problem ist - wenn es nur eine IP gibt, gibt Powershell kein Ein-Element-Array zurück, sondern die IP-Adresse (als Zeichenfolge wie "192.168.0.100") - die Zeichenfolge hat eine .lengthEigenschaft, die größer als 1 ist Der Test besteht und ich erhalte die ersten beiden Zeichen in der Zeichenfolge anstelle der ersten beiden IP-Adressen in der Sammlung.

Wie kann ich Powershell zwingen, eine Sammlung mit einem Element zurückzugeben, oder alternativ feststellen, ob das zurückgegebene "Ding" eher ein Objekt als eine Sammlung ist?

Dylan Beattie
quelle
28
Der am meisten
fehlerhafte
Ich halte Ihr Beispiel für überkompliziert. Einfachere Frage: << $ x = echo Hallo; $ x -is [Array] >> ergibt False.
Raúl Salinas-Monteagudo
Wurde dieses Verhalten in Powershell 5 geändert? Ich habe ein ähnliches Problem, das ich nicht auf 5 reproduzieren kann, aber auf 4
NickL

Antworten:

143

Definieren Sie die Variable auf zwei Arten als Array ...

Schließen Sie Ihre Piped-Befehle in Klammern mit einem @am Anfang ein:

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

Geben Sie den Datentyp der Variablen als Array an:

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

Oder überprüfen Sie den Datentyp der Variablen ...

IF ($ServerIps -isnot [array])
{ <error message> }
ELSE
{ <proceed> }
JNK
quelle
28
Wenn Sie einen Befehl einschließen, @(...)wird ein Array zurückgegeben, auch wenn keine Objekte vorhanden sind. [Array]Wenn Sie das Ergebnis einer Variablen vom Typ -typ zuweisen, wird $ null zurückgegeben, wenn keine Objekte vorhanden sind.
Nic
1
Nur ein Hinweis, dass keine dieser Lösungen funktioniert, wenn das zurückgegebene Objekt ein PSO-Objekt ist (möglicherweise andere).
Deadly-Bagel
2
@ Deadly-Bagel Kannst du ein Beispiel dafür zeigen? Für mich @(...)arbeiten Sie für alle Arten von Objekten richtig (Ergebnis, von dem ich erwarte, dass es erzeugt wird).
user4003407
1
Komisch, wie Sie wieder auf die gleichen Fragen stoßen. Ich hatte (und habe) ein etwas anderes Problem, ja, wie in der Frage funktioniert dies gut, aber wenn ich von einer Funktion zurückkehre, ist es eine andere Geschichte. Wenn es ein Element gibt, wird das Array ignoriert und nur das Element zurückgegeben. Wenn Sie ein Komma vor die Variable setzen, wird es in ein Array gezwungen, aber ein Array mit mehreren Elementen gibt dann ein zweidimensionales Array zurück. Sehr langweilig.
Deadly-Bagel
1
Gah, das ist auch das letzte Mal passiert, jetzt kann ich es nicht wiederholen. Auf jeden Fall habe ich mein aktuelles Problem damit gelöst Return ,$out, dass es immer zu funktionieren scheint. Wenn ich erneut auf das Problem stoße, werde ich ein Beispiel veröffentlichen.
Deadly-Bagel
13

Erzwingen Sie das Ergebnis in ein Array, damit Sie eine Count-Eigenschaft haben können. Einzelne Objekte (skalar) haben keine Count-Eigenschaft. Zeichenfolgen haben eine Längeneigenschaft, sodass Sie möglicherweise falsche Ergebnisse erhalten. Verwenden Sie die Count-Eigenschaft:

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

Verwenden Sie übrigens den Operator -as, anstatt einen Platzhalter zu verwenden, der auch mit Zeichenfolgen übereinstimmen kann:

[array]$serverIps = gwmi Win32_NetworkAdapterConfiguration -filter "IPEnabled=TRUE" | Select-Object -ExpandProperty IPAddress | Where-Object {($_ -as [ipaddress]).AddressFamily -eq 'InterNetwork'}
Shay Levy
quelle
Dafür konnte er nicht auch einfach den Datentyp mit überprüfen -is?
JNK
Strings haben eine .length-Eigenschaft - deshalb funktioniert es ... :)
Dylan Beattie
8

Wenn Sie die Variable vorab als Array deklarieren, können Sie ihr Elemente hinzufügen - auch wenn es nur eines ist ...

Das sollte funktionieren...

$serverIps = @()

gwmi Win32_NetworkAdapterConfiguration 
    | Where { $_.IPAddress } 
    | Select -Expand IPAddress 
    | Where { $_ -like '*.*.*.*' } 
    | Sort | ForEach-Object{$serverIps += $_}
Kyle Neier
quelle
Ich denke tatsächlich, dass dies die klarste und sicherste Option ist. Sie können ".Count - ge 1" zuverlässig für die Sammlung oder "Foreach"
Jaigene Kang
2

Sie können verwenden Measure-Object, um die tatsächliche Objektanzahl abzurufen, ohne auf die CountEigenschaft eines Objekts zurückzugreifen .

$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
}
Patrick
quelle
1

Sie können entweder ein Komma ( ,) vor der Rückgabeliste hinzufügen return ,$listoder umwandeln [Array]oder [YourType[]]an der Stelle, an der Sie die Liste verwenden möchten.

Luckybug
quelle
0

Ich hatte dieses Problem beim Übergeben eines Arrays an eine Azure-Bereitstellungsvorlage. Wenn es ein Objekt gab, "konvertierte" PowerShell es in eine Zeichenfolge. Im folgenden Beispiel $awird von einer Funktion zurückgegeben, bei der VM gemäß dem Wert eines Tags beanstandet wird. Ich übergebe das $aan das New-AzureRmResourceGroupDeploymentCmdlet, indem ich es einwickle @(). Wie so:

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

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

VMObject ist einer der Parameter der Vorlage.

Dies ist möglicherweise nicht die technischste / robusteste Methode, reicht jedoch für Azure aus.


Aktualisieren

Nun, das oben genannte hat funktioniert. Ich habe alle oben genannten und einige ausprobiert, aber der einzige Weg, den ich $vmObjectals Array mit der Bereitstellungsvorlage mit einem Element übergeben konnte, ist wie folgt (ich gehe davon aus, dass MS erneut abgespielt wurde (dies war ein Bericht und behoben) Fehler im Jahr 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 ist die Ausgabe von Get-AzureRmVM.

Ich übergebe $DeserializedJsonden Parameter der Bereitstellungsvorlage (vom Typ Array).

Als Referenz ist der schöne Fehler New-AzureRmResourceGroupDeploymentwirft

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

Gibt als referenziertes Objekt zurück, sodass es beim Übergeben nie konvertiert wird.

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