Powershell 自定义对象混淆

btk*_*tk_ 3 powershell

我正在编写一个脚本,试图输出两个不同的自定义对象;一个接一个地遇到问题,所以为了简化,我把所有的代码都精简到了最低限度:

$beep = new-object -TypeName PSObject
$beep | Add-Member -MemberType NoteProperty -Name "Entry1" -Value "beep1"
$beep | Add-Member -MemberType NoteProperty -Name "Entry2" -Value "beep1"
$beep

$boop = new-object -TypeName PSObject
$boop | Add-Member -MemberType NoteProperty -Name "Entry1" -Value "boop1"
$boop | Add-Member -MemberType NoteProperty -Name "Entry2" -Value "boop1"
$boop
Run Code Online (Sandbox Code Playgroud)

当我运行它时,对象似乎组合在一起。当我做一个 get-member 时,它似乎只显示一个对象。为什么?

在我实际尝试完成的代码中,一个对象是上面的自定义对象,但另一个是 selected.system.int32。当我尝试一个接一个地输出时,只有第一个输出。如果我翻转订单;同样的事情,第一个对象得到输出。我做错了什么/不明白?

@JamesQ - 我想让我感到困惑的是当我这样做时:

$beep = new-object -TypeName PSObject
$beep | Add-Member -MemberType NoteProperty -Name "Entry1" -Value "beep1"
$beep | Add-Member -MemberType NoteProperty -Name "Entry2" -Value "beep1"
$beep | get-member

$boop = new-object -TypeName PSObject
$boop | Add-Member -MemberType NoteProperty -Name "Entry1" -Value "boop1"
$boop | Add-Member -MemberType NoteProperty -Name "Entry2" -Value "boop1"
$boop | get-member
Run Code Online (Sandbox Code Playgroud)

我得到:

TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Entry1      NoteProperty System.String Entry1=beep1
Entry2      NoteProperty System.String Entry2=beep1
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Entry1      NoteProperty System.String Entry1=boop1
Entry2      NoteProperty System.String Entry2=boop1
Run Code Online (Sandbox Code Playgroud)

我试图让问题保持简单,但我认为我把它说得太含糊了,所以......

我正在尝试重用一些代码:

function Get-NetworkConfig($computerName) {
  Get-WmiObject Win32_NetworkAdapter -Filter 'NetConnectionStatus=2' |
    ForEach-Object {
      $result = 1 | Select-Object Name, IP, MAC
      $result.Name = $_.Name
      $result.MAC = $_.MacAddress
      $config = $_.GetRelated('Win32_NetworkAdapterConfiguration') 
      $result.IP = $config | Select-Object -expand IPAddress
      $result
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在同一个脚本中类似:

function Get-ComputerInfo($computerName) {
    $operatingSystem = Get-WMIObject -computername $computerName win32_operatingsystem
    $computerInfo = new-object -TypeName PSObject
    $computerInfo | Add-Member -MemberType NoteProperty -Name "Computer Name" -Value ("$computerName")
    $computerInfo | Add-Member -MemberType NoteProperty -Name "Operating System" -Value ("$operatingSystem.Caption")
    $computerInfo
}

$computerNames = $args
foreach ($computerName in $computerNames) {
    Get-ComputerInfo($computerName)
    Get-NetworkConfig($computerName)
}
Run Code Online (Sandbox Code Playgroud)

只显示 get-computerinfo 的输出,然后是一堆空行,其中 get-networkconfig 将有输出。

如果我颠倒顺序;只有第一个函数给出输出,第二个函数只输出空行。

为什么我不能像这样按顺序调用两个函数?

mkl*_*nt0 5

当我执行 a 时Get-Member,它似乎只显示一个对象。

Get-Member显示有关输入集合中不同类型的信息,这意味着报告每种类型的第一次出现,并跳过后续出现。

在您的情况下,两个输入对象都是 type [System.Management.Automation.PSCustomObject],因此Get-Member将仅报告一种共享类型。

例如,1, 2 | Get-Member报告有关System.Int32 once 的信息。


一个对象是上面的自定义对象,但另一个是 selected.system.int32。当我尝试一个接一个地输出时,只有第一个输出。

PowerShell 的默认输出格式默认为隐式使用,Format-Table用于最多具有 4 个属性的自定义对象。

如果您输出具有不同类型的多个对象,并且第一个对象默认为隐式Format-Table输出,则第一个对象的类型将单独确定在结果表中显示的属性(列)

  • 注意:如果第一个对象的类型碰巧具有与其关联的格式数据(如 所报告的那样Get-FormatData),后续对象打印,尽管总是通过隐式Format-List格式设置。

如果后续对象没有第一个对象具有的任何相同属性,则它们只需打印一个空行;如果它们具有某些相同的属性,则仅打印它们;任何附加属性都将被忽略。

需要注意的是,这只是一个显示问题,但是:输出的所有对象仍然存在;如果您将输出发送到另一个命令进行进一步处理而不是直接发送到控制台,它们都会在那里。

一个简单的例子:

PS> [pscustomobject] @{ one = 1; two = 2 }, [pscustomobject] @{ three = 3 }
    
one two
--- ---
  1   2
 
Run Code Online (Sandbox Code Playgroud)

请注意第二个自定义对象是如何产生一个空行的,因为它既没有属性.one也没有.two.

您可以通过使用应用于每个对象的显式格式化命令来解决这个问题

PS> [pscustomobject] @{ one = 1; two = 2 }, [pscustomobject] @{ three = 3 } |
      ForEach-Object { Format-Table -InputObject $_ }

one two
--- ---
  1   2



three
-----
    3
Run Code Online (Sandbox Code Playgroud)

与单个输出命令相同的方法:

[pscustomobject] @{ one = 1; two = 2 } | Format-Table
[pscustomobject] @{ three = 3 } | Format-Table
Run Code Online (Sandbox Code Playgroud)

正如Mark Wragg 的博客文章所解释的那样,给定脚本生成的所有输出——即使是跨单独的命令——都被发送到同一个管道
(您可以将交互式提交的命令行视为隐式脚本。)

有关如何根据显示格式处理单个管道中的混合类型的更详细讨论,请参阅此答案


为什么使用显式格式化命令有帮助:

通过显式管道到Format-*cmdlet(例如,[pscustomobject] @{ one = 1; two = 2 } | Format-Table),您实际上是将格式化对象[Microsoft.PowerShell.Commands.Internal.Format.*]各种类型)发送到管道,然后 PowerShell 有效地传递它们以进行显示。

另一种方法是使用通用的解决方法:如果您改为使用管道Out-Host(例如,[pscustomobject] @{ one = 1; two = 2 } | Out-Host),在这种情况下:

  • 您绕过管道并直接打印到控制台(如果您在常规控制台窗口中运行 PowerShell),
  • 并应用对象的默认格式视图。

需要注意的是,这些变通方法仅适用于显示目的,因为原始对象在此过程中丢失了:

  • 当您Format-*显式地通过管道传输到cmdlet 时,您将使用包含格式说明的对象替换原始对象,这些对象对于进一步处理毫无用处。

  • 当您通过管道传输到 时Out-Host,您不会向脚本的管道发送任何内容