ฉันจะบังคับให้ Powershell ส่งคืนอาร์เรย์ได้อย่างไรเมื่อการเรียกส่งคืนวัตถุเพียงชิ้นเดียว


123

ฉันใช้ Powershell เพื่อตั้งค่าการผูก 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 ส่งคืนคอลเล็กชันองค์ประกอบเดียวหรืออีกทางเลือกหนึ่งว่า "สิ่ง" ที่ส่งคืนเป็นวัตถุไม่ใช่คอลเล็กชันหรือไม่


29
ด้านเดียวที่น่ารำคาญที่สุด / ข้อผิดพลาดของ PowerShell ..
user2864740

ฉันถือว่าตัวอย่างของคุณซับซ้อนเกินไป คำถามที่ง่ายกว่า: << $ x = echo สวัสดี; $ x -is [Array] >> ให้ผลเป็น False
Raúl Salinas-Monteagudo

พฤติกรรมนี้เปลี่ยนไปหรือไม่ใน powershell 5 ฉันมีปัญหาคล้าย ๆ กันที่ไม่สามารถทำซ้ำได้ในวันที่ 5 แต่ทำได้ในวันที่ 4
NickL

คำตอบ:


143

กำหนดตัวแปรเป็นอาร์เรย์หนึ่งในสองวิธี ...

รวมคำสั่ง piped ของคุณไว้ในวงเล็บโดยมี@จุดเริ่มต้น:

$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]ตัวแปร -typed จะยังคงคืนค่า $ null หากไม่มีวัตถุเป็นศูนย์
Nic

1
โปรดทราบว่าวิธีแก้ปัญหาเหล่านี้ไม่สามารถใช้งานได้หากวัตถุที่ส่งคืนเป็น PSObject (อาจเป็นอย่างอื่น)
Deadly-Bagel

2
@ Deadly-Bagel คุณสามารถแสดงตัวอย่างนี้ได้หรือไม่? สำหรับฉัน@(...)ทำงานได้อย่างถูกต้อง (สร้างผลลัพธ์ฉันคาดหวังว่ามันควรจะสร้าง) สำหรับวัตถุทุกประเภท
user4003407

1
ตลกดีที่คุณกลับมาตอบคำถามเดิม ๆ ฉันมี (และมีอีกครั้ง) ปัญหาที่แตกต่างกันเล็กน้อยใช่ในคำถามนี้ใช้ได้ดี แต่เมื่อกลับมาจากฟังก์ชั่นมันเป็นเรื่องที่แตกต่าง หากมีองค์ประกอบเดียวอาร์เรย์จะถูกละเว้นและส่งคืนเฉพาะองค์ประกอบเท่านั้น หากคุณใส่ลูกน้ำก่อนตัวแปรจะบังคับให้อาร์เรย์ แต่อาร์เรย์หลายองค์ประกอบจะส่งคืนอาร์เรย์สองมิติ น่าเบื่อมาก
Deadly-Bagel

1
นี่คือสิ่งที่เกิดขึ้นครั้งสุดท้ายเช่นกันตอนนี้ฉันไม่สามารถทำซ้ำได้ ไม่ว่าในกรณีใดก็ตามฉันแก้ไขปัญหาล่าสุดของฉันโดยใช้Return ,$outสิ่งที่ดูเหมือนจะใช้ได้เสมอ ถ้าฉันพบปัญหาอีกฉันจะโพสต์ตัวอย่าง
Deadly-Bagel

14

บังคับให้ผลลัพธ์เป็น 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'}

สำหรับเรื่องนี้เขาไม่สามารถตรวจสอบประเภทข้อมูลด้วย-is?
JNK

สตริงมีคุณสมบัติ. ความยาว - นั่นคือเหตุผลที่มันใช้งานได้ ... :)
Dylan Beattie

8

หากคุณประกาศตัวแปรเป็นอาร์เรย์ล่วงหน้าคุณสามารถเพิ่มองค์ประกอบเข้าไปได้แม้ว่าจะเป็นเพียงตัวเดียว ...

นี่น่าจะใช้ได้ ...

$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 หากมีวัตถุหนึ่งชิ้น PowerShell จะ "แปลง" เป็นสตริง ในตัวอย่างด้านล่าง$aจะส่งคืนจากฟังก์ชันที่ได้รับ VM ที่คัดค้านตามค่าของแท็ก ผมผ่าน$aไปNew-AzureRmResourceGroupDeploymentcmdlet @()โดยการตัดใน ชอบมาก:

$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.."

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.