Как заставить 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 вернуть одноэлементную коллекцию или, наоборот, определить, является ли возвращенная "вещь" объектом, а не коллекцией?
Ответы
Ответ 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[]]
там, где вы склонны использовать список.