ChatGPT解决这个技术问题 Extra ChatGPT

当调用仅返回一个对象时,如何强制 Powershell 返回一个数组?

我正在使用 Powershell 在 Web 服务器上设置 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 返回单元素集合,或者确定返回的“事物”是否是对象而不是集合?

PowerShell 中最烦人/漏洞百出的方面。
我认为你的例子过于复杂。更简单的问题:<<$x = echo Hello; $x -is [Array]>> 产生 False。
这种行为在 powershell 5 中是否发生了变化?我有一个类似的问题,我无法在 5 上重现,但可以在 4 上重现

J
JNK

以两种方式之一将变量定义为数组...

将您的管道命令括在括号中,并在开头使用 @

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

即使有零个对象,在 @(...) 中包装命令也会返回一个数组。而将结果分配给 [Array] 类型的变量仍将返回 $null 如果有零个对象。
请注意,如果返回的对象是 PSObject(可能是其他对象),则这些解决方案都不起作用。
@Deadly-Bagel 你能举个例子吗?对我来说,@(...) 对于任何类型的对象都可以正常工作(产生我期望它应该产生的结果)。
有趣的是,您最终如何回到相同的问题。我有(并且再次遇到)一个稍微不同的问题,是的,因为这个问题很好,但是当从一个函数返回时,它是一个不同的故事。如果只有一个元素,则忽略该数组,只返回该元素。如果你在变量前加一个逗号,它会强制它为一个数组,但一个多元素数组将返回一个二维数组。很乏味。
啊,这也是上次发生的事情,现在我无法复制它。无论如何,我通过使用似乎总是有效的 Return ,$out 解决了我最近的问题。如果我再次遇到问题,我会发布一个示例。
S
Shay Levy

将结果强制为 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 检查数据类型吗?
字符串具有 .length 属性-这就是它起作用的原因... :)
@($serverIps).Count 是不必要的。使用 $serverIps.Count 即可。
L
Luckybug

您可以在返回列表之前添加一个逗号 (,),例如 return ,$list,或者在您倾向于使用该列表的位置添加 [Array][YourType[]]


只需转换为数组。很简单。惊人的!最佳答案!这个有效的逗号困扰着我。
在我总是试图从函数返回数组的情况下,即使只有一个元素,我也只能使用逗号 , (一元数组运算符)取得成功。
那么,,(...), @(...) 和 [array](...) 之间有什么区别。在以下示例中 - $strArray|%{ $_ -split ' ' } - 返回数组列表的唯一选项是 $strArray|%{ ,($_ -split ' ') }。我正在运行 Powershell 5
J
JNK

如果您提前将变量声明为数组,则可以向其中添加元素 - 即使它只是一个...

这应该工作...

$serverIps = @()

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

我实际上觉得这是最清晰和最安全的选择。您可以在集合或“Foreach”上可靠地使用“.Count - ge 1”
增量构建数组是不好的做法,因为每个周期都会创建并填充一个新数组。对于大型数组,这非常低效。当前的首选选项是使用类型化列表对象和 .Add() $List = [System.Collections.Generic.List[string]]::new() # OR $list = New-Object System.Collections.Generic。列表[字符串] $List.Add("Test1")
P
Patrick

您可以使用 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
}

Measure-Object 是不必要的。使用 $serverIps.Count 即可。
如果只有一个对象,则 Count 属性不可用。这是原始问题的全部要点。 Measure-Object 将保证具有多个、单个甚至 $null 对象的有效 Count 属性。
正如我所测试的,每个对象都有一个 .Count 属性。 function Get1 { return @('Hello') }; (Get1).GetType(); (Get1).Count
我意识到几乎所有对象都只支持 PowerShell Core .Count
m
masato

作为引用对象返回,因此它在传递时永远不会转换。

return @{ Value = @("single data") }

C
Community

我在将数组传递给 Azure 部署模板时遇到了这个问题。如果有一个对象,PowerShell 会将其“转换”为字符串。在下面的示例中,$a 从根据标记值获取 VM 对象的函数返回。我通过将 $a 包装在 @() 中将其传递给 New-AzureRmResourceGroupDeployment cmdlet。像这样:

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

W
Will Huang

有一种方法可以处理您的情况。让大部分代码保持原样,只需更改处理 $serverIps 对象的方式。这段代码可以处理$null,只有一个项目,很多项目。

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

# Always use ".Count" instead of ".Length".
# This works on $null, only one item, or many items.
if ($serverIps.Count -le 1) {
    Write-Host "You need at least 2 IP addresses for this to work!"
    exit
}

# Always use foreach on a array-possible object, so that
# you don't have deal with this issue anymore.
$serverIps | foreach {
    # The $serverIps could be $null. Even $null can loop once.
    # So we need to skip the $null condition.
    if ($_ -ne $null) {
        # Get the index of the array.
        # The @($serverIps) make sure it must be an array.
        $idx = @($serverIps).IndexOf($item)

        if ($idx -eq 0) { $primaryIp = $_ }
        if ($idx -eq 1) { $secondaryIp = $_ }
    }
}

在 PowerShell Core 中,每个对象都有一个 .Count 属性。在 Windows PowerShell 中,“几乎”每个对象都有一个 .Count 属性。