Làm cách nào để buộc Powershell trả về một mảng khi một lệnh gọi chỉ trả về một đối tượng?


123

Tôi đang sử dụng Powershell để thiết lập liên kết IIS trên máy chủ web và gặp sự cố với mã sau:

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

Nếu có hơn 2 IP trên máy chủ, tốt thôi - Powershell trả về một mảng và tôi có thể truy vấn độ dài mảng và trích xuất địa chỉ đầu tiên và thứ hai.

Vấn đề là - nếu chỉ có một IP, Powershell không trả về mảng một phần tử, nó sẽ trả về địa chỉ IP (dưới dạng một chuỗi, như "192.168.0.100") - chuỗi có một thuộc .lengthtính, nó lớn hơn 1, vì vậy kiểm tra vượt qua và tôi kết thúc với hai ký tự đầu tiên trong chuỗi, thay vì hai địa chỉ IP đầu tiên trong bộ sưu tập.

Làm cách nào để tôi có thể buộc Powershell trả về một tập hợp một phần tử hoặc xác định xem "điều" được trả về có phải là một đối tượng hơn là một tập hợp không?


28
Hầu hết các đơn thương độc mã gây phiền nhiễu / bug-ridden khía cạnh của PowerShell ..
user2864740

Tôi cho rằng ví dụ của bạn là quá phức tạp. Câu hỏi đơn giản hơn: << $ x = echo Xin chào; $ x -is [Array] >> cho kết quả là False.
Raúl Salinas-Monteagudo

hành vi này có được thay đổi trong powershell 5 không? tôi đã gặp sự cố tương tự mà tôi không thể tái tạo vào ngày 5, nhưng có thể xảy ra vào ngày 4
NickL

Câu trả lời:


143

Định nghĩa biến dưới dạng mảng theo một trong hai cách ...

Gói các lệnh được định hình của bạn trong dấu ngoặc đơn với dấu @ở đầu:

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

Chỉ định kiểu dữ liệu của biến là một mảng:

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

Hoặc, kiểm tra kiểu dữ liệu của biến ...

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

28
Gói một lệnh trong @(...)sẽ trả về một mảng ngay cả khi không có đối tượng nào. Trong khi việc gán kết quả cho một [Array]biến -typed sẽ vẫn trả về $ null nếu không có đối tượng nào.
Nic

1
Chỉ cần lưu ý rằng không có giải pháp nào trong số các giải pháp này hoạt động nếu đối tượng được trả về là một PSObject (có thể là những giải pháp khác).
Deadly-Bagel

2
@ Deadly-Bagel Bạn có thể cho ví dụ về điều này không? Đối với tôi, @(...)làm việc đúng cách (tạo ra kết quả mà tôi mong đợi nó sẽ tạo ra) cho bất kỳ loại đối tượng nào.
user4003407

1
Thật buồn cười khi bạn kết thúc với những câu hỏi tương tự. Tôi đã gặp (và một lần nữa) một vấn đề hơi khác, vâng như trong câu hỏi, điều này hoạt động tốt nhưng khi quay lại từ một hàm thì lại là một câu chuyện khác. Nếu có một phần tử, mảng sẽ bị bỏ qua và chỉ phần tử được trả về. Nếu bạn đặt dấu phẩy trước biến, nó buộc nó vào một mảng nhưng sau đó một mảng nhiều phần tử sẽ trả về một mảng hai chiều. Rất tẻ nhạt.
Deadly-Bagel

1
Gah, đây là những gì đã xảy ra lần trước, bây giờ tôi không thể tái tạo nó. Ở bất kỳ mức độ nào, tôi đã giải quyết vấn đề gần đây của mình bằng cách sử dụng Return ,$outnó dường như luôn hoạt động. Nếu tôi gặp sự cố một lần nữa, tôi sẽ đăng một ví dụ.
Deadly-Bagel

13

Buộc kết quả vào một Mảng để bạn có thể có thuộc tính Đếm. Các đối tượng đơn lẻ (vô hướng) không có thuộc tính Đếm. Các chuỗi có thuộc tính độ dài nên bạn có thể nhận được kết quả sai, hãy sử dụng thuộc tính Count:

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

Nhân tiện, thay vì sử dụng ký tự đại diện cũng có thể khớp với chuỗi, hãy sử dụng toán tử -as:

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

Đối với điều này, anh ta không thể chỉ kiểm tra kiểu dữ liệu với -is?
JNK

Strings có một tài sản .length - đó là lý do tại sao nó làm việc ... :)
Dylan Beattie

8

Nếu bạn khai báo biến dưới dạng một mảng trước, bạn có thể thêm các phần tử vào đó - ngay cả khi nó chỉ là một ...

Điều này sẽ hoạt động ...

$serverIps = @()

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

Tôi thực sự cảm thấy đây là lựa chọn rõ ràng và an toàn nhất. Bạn chắc chắn có thể sử dụng" Count - ge 1' vào bộ sưu tập hoặc 'Foreach'
Jaigene Kang

2

Bạn có thể sử dụng Measure-Objectđể lấy số lượng đối tượng thực tế mà không cần dùng đến thuộc Counttính của đối tượng .

$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

Bạn có thể thêm dấu phẩy ( ,) trước danh sách trả về như return ,$listhoặc ép kiểu nó [Array]hoặc [YourType[]]tại nơi bạn có xu hướng sử dụng danh sách.


0

Tôi gặp sự cố này khi chuyển một mảng sang mẫu triển khai Azure. Nếu có một đối tượng, PowerShell "chuyển đổi" nó thành một chuỗi. Trong ví dụ dưới đây, $ađược trả về từ một hàm được VM phản đối theo giá trị của thẻ. Tôi chuyển lệnh $atới New-AzureRmResourceGroupDeploymentlệnh ghép ngắn bằng cách gói nó vào @(). Như vậy:

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

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

VMObject là một trong những tham số của mẫu.

Có thể không phải là cách kỹ thuật / mạnh mẽ nhất để làm điều đó, nhưng nó đủ cho Azure.


Cập nhật

Vâng ở trên đã làm việc. Tôi đã thử tất cả các cách trên và một số cách, nhưng cách duy nhất tôi quản lý để chuyển $vmObjectdưới dạng một mảng, tương thích với mẫu triển khai, với một phần tử như sau (Tôi hy vọng MS đã chơi lại (đây là một báo cáo và đã được sửa lỗi trong năm 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 là đầu ra của Get-AzureRmVM.

Tôi chuyển $DeserializedJsoncho tham số của mẫu triển khai (thuộc kiểu mảng).

Để tham khảo, các lỗi đáng yêu 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

Trả về dưới dạng một đối tượng được tham chiếu, vì vậy nó không bao giờ được chuyển đổi trong khi truyền.

return @{ Value = @("single data") }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.