Як я можу змусити 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 повернути одноелементну колекцію, або альтернативно визначити, чи повертається "річ" є об'єктом, а не колекцією?


28
Більшість дратівливих / помилок, пов'язаних з PowerShell ..
user2864740

Я вважаю ваш приклад надскладним. Простіше питання: << $ x = відлуння Привіт; $ x - це [масив] >> дає хибність.
Рауль Салінас-Монтеагудо

чи змінилася така поведінка в папері 5? У мене є аналогічне питання, яке я не можу відтворити 5, але можна 4
NickL

Відповіді:


143

Визначте змінну як масив одним із двох способів ...

Оберніть свої трубопровідні команди в дужки з @на початку:

$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, якщо є нульові об'єкти.
Нік

1
Лише зауважте, що жодне з цих рішень не працює, якщо об'єкт, що повертається, є об'єктом PSO (можливо, іншими).
Смертельно-Багель

2
@ Deadly-Bagel Чи можете ви показати приклад цього? Для мене @(...)працюють належним чином (дають результат, я думаю, він повинен давати) для будь-яких типів об'єктів.
user4003407

1
Смішно, як ти знову опинишся над тими ж питаннями. У мене була (і в мене знову) дещо інша проблема, так, як у питанні, це добре працює, але коли повертається з функції, це вже інша історія. Якщо є один елемент, масив ігнорується і повертається лише елемент. Якщо ви ставите кому перед змінною, вона примушує її до масиву, але багатоелементний масив поверне двовимірний масив. Дуже нудно.
Смертельно-Багель

1
Га, це теж трапилось минулого разу, тепер я не можу це повторити. У будь-якому випадку я вирішив свою недавню проблему, використовуючи Return ,$outяку, здається, завжди працює. Якщо я знову зіткнуся з проблемою, я викладу приклад.
Смертельно-Бейгель

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

Струни мають властивість .length - ось чому це працює ... :)
Ділан Бітті

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-AzureRmResourceGroupDeploymentкомандлет, оточивши його @(). Так:

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

0

Повертайтеся як посиланий об'єкт, тому він ніколи не перетворюється під час проходження.

return @{ Value = @("single data") }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.