尝试通过引用传递自定义对象数组但失败

发布于 2025-01-20 11:13:12 字数 842 浏览 1 评论 0 原文

我正在在PowerShell脚本中创建一个自定义对象数组

$UnreachablePCs = [PSCustomObject]@()

,然后我将其传递到像This $ TraderWorkStations这样的函数中

function GetComputerData {
param (
    $Computers, [ref]$unreachable
)
...
$unreachablePC = [PSCustomObject]@{
            ComputerName = $i.DNSHostName
            CPU = "n/a"
            Cores = "n/a"
            IP = "n/a"
            Memory = "n/a"
            Uptime = "n/a"
            OS = "n/a"
            Board = "n/a"
        }
    $UnreachablePCs += $unreachablePC
    Write-Output $UnreachablePCs.Count
...
}


GetComputerData -Computers $TraderWorkstations -unreachable ([ref]$UnreachablePCs)
Write-Output $UnreachablePCs.Count

,这是PC的列表,这些PC在功能中迭代。所有无法触及的PC都添加到功能中的其他分支中的$ UnterablePCS数组中。在函数中,我调用的.COUNT将随着工作站添加到列表中时会增加。但是,在该功能称为最终。计数返回0。我在这里缺少什么?

I am creating an array of custom objects in my powershell script

$UnreachablePCs = [PSCustomObject]@()

Then I am passing this into a function like this

function GetComputerData {
param (
    $Computers, [ref]$unreachable
)
...
$unreachablePC = [PSCustomObject]@{
            ComputerName = $i.DNSHostName
            CPU = "n/a"
            Cores = "n/a"
            IP = "n/a"
            Memory = "n/a"
            Uptime = "n/a"
            OS = "n/a"
            Board = "n/a"
        }
    $UnreachablePCs += $unreachablePC
    Write-Output $UnreachablePCs.Count
...
}


GetComputerData -Computers $TraderWorkstations -unreachable ([ref]$UnreachablePCs)
Write-Output $UnreachablePCs.Count

$TraderWorkstations is a list of pcs which are iterated over in the function. All pcs that aren't reachable are added to the $UnreachablePCs array in an else branch in the function. In the function the .Count I'm calling will increment as workstations are added to the list. But After the function is called the final .Count is returning 0. What am I missing here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

找个人就嫁了吧 2025-01-27 11:13:12

不要使用 [ref] powershell代码中的参数 [ref] 的目的是促进呼叫.net api方法;在PowerShell代码中,它在语法上很尴尬,可能导致微妙的错误,例如您的情况 - 请参阅此答案 [ref] 使用时使用时。

相反,使您的函数 output 构成结果数组的对象(可能是一个一个),让PowerShell在数组中为您收集它们

function GetComputerData {
param (
    $Computers # NO [ref] parameter
)
   # ...
   # Output (one or more) [pscustomobject] instances.
   [PSCustomObject]@{
            ComputerName = $i.DNSHostName
            CPU = "n/a"
            Cores = "n/a"
            IP = "n/a"
            Memory = "n/a"
            Uptime = "n/a"
            OS = "n/a"
            Board = "n/a"
        }
  # ...
}

# Collect the [pscustomobject] instances output by the function
# in an array.
$UnReachablePCs = @(GetComputerData -Computers $TraderWorkstations)

@() array-subexpression operator ,始终创建 [object [object []] array。为了创建一个强大的键入数组,请使用:

[pscustomObject []] $ unrachablepcs = getComputerData -computers $ traderWorkStations

  • 由于PowerShell的隐式输出功能,该功能直接产生 output ,其中未捕获或重定向的任何命令或表达式自动有助于封闭function's(script的)输出(写于PowerShell的) - 参见此答案以获取详细信息。所有写入函数成功输出流的对象均由变量分配捕获,例如 $ unrachablepcs = ...

  • nore-code> nore-code> notup empacklicit (很少需要)写入成功输出流,这也意味着您不能将其用于临时调试到显示的临时调试,因为它的输出也变成了函数的“返回值”的一部分(对象发送到了成功输出流)。


  • 如果您不需要“不污染”成功输出流的待办事项输出,请使用 写入 - 霍斯特 。最好使用针对其他特定于特定输出的CMDLET,例如 write-verbose witr-debug ,尽管它们两个都需要 opt-In-In 产生可见的输出(请参阅链接的文档) 。


至于您的原始方法的问题

$ unrachablepcs = [pscustomObject]@()

这不会创建自定义对象的数组。
相反,它创建了一个 [object []] 数组(无用,大部分是看不见的)包裹在 [psObject] 实例中。 [1] >

使用以下内容:

[PSCustomObject[]] $UnreachablePCs = @()

对于使用 [Ref] ,带有+= > 的数组变量:

  • 从根本上讲,您需要更新A(参数)变量包含a /code> 实例通过分配给其 .value 属性,而不是 [ref] 实例全部( $ unteachpcs .value = ... 而不是 $ unreachpcs = ...

  • 但是, = 技术应避免,除了小数组,因为每个+= 操作都需要在场景后面创建 new 阵列(包括原始元素)和新的(S)),这是必要的,因为数组为固定大小数据结构。

    • 要么:使用有效扩展的列表数据类型,例如 [System.Collections.generic.generic.list [pscustomObject]] 并通过其 .add() /code>方法(实际上,如果您事先创建列表实例,则可以将其作为普通(non- [ref] )参数传递给非 - [ref] 参数,并且该函数仍将直接更新列表,这是因为在调用 .add()时在同一列表实例上操作顶部通常仍然可取):

        $ unrachablepcs = [System.Collections.generic.list [pscustomObject]] @()()
      foreach($ i in 1..2){
        $ unrachablepcs.add([pscustomObject] @{foo = $ i})
      }
       

    • 或 - 最好 - 在可能的情况下:使用PowerShell的循环语句用作 empressions ,然后让PowerShell为您收集所有输出(也显示了从上面的整个函数中显示的) ;例如:

       ##自动通过循环收集两个自定义对象。
      [array] $ unrachablepcs = foreach($ i in 1..2){
         [pscustomObject] @{foo = $ i}
      }
       



[1]这种行为是不幸的,但源于类型加速器 [pscustomObject] [psObject] same [pscustomObject] 铸件仅在创建单个自定义对象字面的的上下文中有意义工作foo = 1} )。在所有其他情况下,都会发生 [psObject] 中的大多不可见包装;例如, [pscustomObject] @()-is [object []] is $ true - ie,结果的行为就像常规数组一样 - 但是 [pscustomobignt] @()-is [psobject] is $ true ,指示存在 [PSOBJECT] wrapper。 sup>

Don't use [ref] parameters in PowerShell code: [ref]'s purpose is to facilitate calling .NET API methods; in PowerShell code, it is syntactically awkward and can lead to subtle bugs, such as in your case - see this answer guidance on when [ref] use is appropriate.

Instead, make your function output the objects that make up the result array (possibly one by one), and let PowerShell collect them for you in an array:

function GetComputerData {
param (
    $Computers # NO [ref] parameter
)
   # ...
   # Output (one or more) [pscustomobject] instances.
   [PSCustomObject]@{
            ComputerName = $i.DNSHostName
            CPU = "n/a"
            Cores = "n/a"
            IP = "n/a"
            Memory = "n/a"
            Uptime = "n/a"
            OS = "n/a"
            Board = "n/a"
        }
  # ...
}

# Collect the [pscustomobject] instances output by the function
# in an array.
$UnReachablePCs = @(GetComputerData -Computers $TraderWorkstations)

@(), the array-subexpression operator, always creates an [object[]] array. To create a strongly typed array instead, use:

[pscustomobject[]] $unreachablePCs = GetComputerData -Computers $TraderWorkstations

Important:

  • [PSCustomObject]@{ ... directly produces output from the function, due to PowerShell's implicit output feature, where any command or expression whose output isn't captured or redirected automatically contributes to the enclosing function's (script's) output (written to PowerShell's success output stream) - see this answer for details. All the objects written to the function's success output streams are captured by a variable assignment such as $UnReachablePCs = ...

  • Write-Output is the explicit (rarely needed) way to write to the success output stream, which also implies that you cannot use it for ad hoc debugging output to the display, because its output too becomes part of the function's "return value" (the objects sent to the success output stream).

  • If you want to-display output that doesn't "pollute" the success output stream, use Write-Host. Preferably, use cmdlets that target other, purpose-specific output streams, such as Write-Verbose and Write-Debug, though both of them require opt-in to produce visible output (see the linked docs).


As for the problems with your original approach:

$UnreachablePCs = [PSCustomObject]@()

This doesn't create an array of custom objects.
Instead, it creates an [object[]] array that is (uselessly, mostly invisibly) wrapped in a [psobject] instance.[1]

Use the following instead:

[PSCustomObject[]] $UnreachablePCs = @()

As for use of [ref] with an array variable updated with +=:

  • Fundamentally, you need to update a (parameter) variable containing a [ref] instance by assigning to its .Value property, not to the [ref] instance as a whole ($UnreachablePCs.Value = ... rather than $UnreachablePCs = ...)

  • However, the += technique should be avoided except for small arrays, because every += operation requires creating a new array behind the scenes (comprising the original elements and the new one(s)), which is necessary, because arrays are fixed-size data structures.

    • Either: Use an efficiently extensible list data type, such as [System.Collections.Generic.List[PSCustomObject]] and grow it via its .Add() method (in fact, if you create the list instance beforehand, you could pass it as a normal (non-[ref]) argument to a non-[ref] parameter, and the function would still directly update the list, due to operating on the same list instance when calling .Add() - that said, the output approach at the top is generally still preferable):

      $unreachablePCs = [System.Collections.Generic.List[PSCustomObject]] @()
      foreach ($i in 1..2) {
        $unreachablePCs.Add([pscustomobject] @{ foo = $i })
      }
      
    • Or - preferably - when possible: Use PowerShell's loop statements as expressions, and let PowerShell collect all outputs in an array for you (as also shown with output from a whole function above); e.g.:

      # Automatically collects the two custom objects output by the loop.
      [array] $unreachablePCs = foreach ($i in 1..2) {
         [pscustomobject] @{ foo = $i }
      }
      

[1] This behavior is unfortunate, but stems from the type accelerators [pscustomobject] and [psobject] being the same. A [pscustomobject] cast only works meaningfully in the context of creating a single custom object literal, i.e. if followed by a hashtable (e.g., [pscustomobject] @{ foo = 1 }). In all other cases, the mostly invisible wrapping in [psobject] occurs; e.g., [pscustomobject] @() -is [object[]] is $true - i.e., the result behaves like a regular array - but [pscustomobject] @() -is [psobject] is also $true, indicating the presence of a [psobject] wrapper.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文