Подтвердить что ты не робот

Как заставить ForceShell возвращать массив, когда вызов возвращает только один объект?

Я использую 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-адреса, fine - Powershell возвращает массив, и я могу запросить длину массива и извлечь только первый и второй адреса.

Проблема - если есть только один IP, Powershell не возвращает одноэлементный массив, он возвращает IP-адрес (как строку, например "192.168.0.100" ) - строка имеет свойство .length он больше 1, поэтому тест проходит, и я получаю первые два символа в строке, а не первые два IP-адреса в коллекции.

Как я могу заставить Powershell вернуть одноэлементную коллекцию или, наоборот, определить, является ли возвращенная "вещь" объектом, а не коллекцией?

4b9b3361

Ответ 1

Определить переменную как массив одним из двух способов...

Вставьте скопированные команды в круглые скобки с помощью @ в начале:

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

Ответ 2

Принудите результат к массиву, чтобы иметь свойство Count. Отдельные объекты (скалярные) не имеют свойства Count. Строки имеют свойство length, поэтому вы можете получить ложные результаты, используйте свойство 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'}

Ответ 3

Если вы объявите переменную как массив раньше времени, вы можете добавить к ней элементы - даже если это всего лишь один...

Это должно работать...

$serverIps = @()

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

Ответ 4

Вы можете использовать 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
}

Ответ 5

У меня возникла проблема передачи массива в шаблон развертывания Azure. Если бы существовал один объект, PowerShell "преобразовал" его в строку. В приведенном ниже примере $a возвращается из функции, которая получает VM в соответствии с значением тега. Я передаю $a в командлет New-AzureRmResourceGroupDeployment, обернув его в @(). Например:

[email protected]{
     [email protected]($a)
}

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

VMObject является одним из параметров шаблона.

Возможно, это не самый технический/надежный способ сделать это, но этого достаточно для Azure.


Update

Хорошо, что выше работало. Я пробовал все вышеперечисленное и некоторые, но единственный способ, которым мне удалось передать $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.."

Ответ 6

Вы можете добавить запятую (,) перед списком return ,$list например return ,$list или привести его к [Array] или [YourType[]] там, где вы склонны использовать список.