呼び出しが1つのオブジェクトのみを返すときに、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は配列を返します。配列の長さをクエリして、最初と2番目のアドレスを問題なく抽出できます。

問題は-IPが1つしかない場合、Powershellは1要素の配列を返さず、IPアドレスを返します(「192.168.0.100」のような文字列として)-文字列には.lengthプロパティがあり、1より大きいため、テストに合格すると、コレクションの最初の2つのIPアドレスではなく、文字列の最初の2文字で終了します。

Powershellに1要素のコレクションを強制的に返すように強制したり、返された「もの」がコレクションではなくオブジェクトであるかどうかをどのように判断したりできますか?


28
PowerShellの最も独力的な迷惑/バグが多い側面
ユーザー2864740

あなたの例は複雑すぎると思います。より簡単な質問:<< $ x = echo Hello; $ x -is [配列] >>はFalseを返します。
ラウル・サリナス- Monteagudo

この動作はPowerShell 5で変更されましたか?5で再現できない同様の問題がありますが、4では再現できます
NickL

回答:


143

2つの方法のいずれかで変数を配列として定義します...

パイプされたコマンドを括弧で@始め、先頭をで囲みます。

$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]指定された変数に結果を代入しても、オブジェクトがゼロの場合は$ nullが返されます。
ニック

1
返されるオブジェクトがPSObject(おそらく他のオブジェクト)である場合、これらのソリューションはどれも機能しないことに注意してください。
Deadly-Bagel、

2
@ Deadly-Bagelこの例を見せていただけますか?私にとっては@(...)、どのような種類のオブジェクトでも適切に機能します(結果が出るはずです)。
user4003407 2016年

1
あなたが同じ質問に戻る方法がおかしい。私はわずかに異なる問題を抱えていました(そして再び持っています)、はい、質問ではこれはうまくいきますが、関数から戻るときは別の話です。要素が1つある場合、配列は無視され、要素のみが返されます。変数の前にコンマを置くと、強制的に配列になりますが、多要素配列は2次元配列を返します。とても退屈です。
Deadly-Bagel

1
ええと、これは前回も起こったことですが、今は再現できません。とにかくReturn ,$out、いつもうまくいくように見えるを使って、最近の問題を解決しました。問題が再度発生した場合は、例を掲載します。
Deadly-Bagel 2017年

13

結果を強制的に配列にして、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'}

これのために、彼はまた、データ型を-is
JNK、2012年

文字列には.lengthプロパティがあります-それが機能している理由です... :)
Dylan Beattie

8

事前に変数を配列として宣言すると、要素を1つだけでも要素に追加できます...

これはうまくいくはずです...

$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配置テンプレートに配列を渡すときにこの問題が発生しました。オブジェクトが1つある場合、PowerShellはそれを文字列に「変換」しました。以下の例で$aは、タグの値に応じてオブジェクト化されたVMを取得する関数からが返されます。でラップ$aしてNew-AzureRmResourceGroupDeploymentコマンドレットに渡し@()ます。そのようです:

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

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

VMObject テンプレートのパラメータの1つです。

それを行うには、最も技術的で堅牢な方法ではないかもしれませんが、Azureにはそれで十分です。


更新

さて、上記はうまくいきました。私は上記のすべてといくつかを試しましたが、$vmObject1つの要素を使用して、配置テンプレートと互換性のある配列として渡すことができた唯一の方法は次のとおりです(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") }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.