【问题标题】:Powershell Custom Object ConfusionPowershell 自定义对象混淆
【发布时间】:2017-08-16 02:10:41
【问题描述】:

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

$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

当我运行它时,对象似乎组合在一起。当我做一个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

我明白了:

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

我试图让问题保持简单,但我认为我把它弄得太模棱两可了,所以......

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

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

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

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

只显示 get-computerinfo 的输出,然后是 get-networkconfig 会输出的一堆空行。

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

为什么我不能像这样依次调用两个函数?

【问题讨论】:

  • 很大程度上取决于您尝试显示对象的方式。我运行了上面的代码,正如预期的那样,得到了两个不同的对象。 Get-Member (一次)只需要一个对象,所以我不明白你关于“显示一个对象”的说法。尝试运行不同的排列来说服自己你有多个对象:Write-Output "beep Entry1 = $($Beep.Entry1)"
  • 如果你想同时输出两个尝试做return $beep, $boop
  • 如果您从存储在 csv 文件中的数据执行 import-csv,您将获得一个 PSCustomObjects 数组。题外话,但还是值得学习。您可能想用 $beep 和 $boop 制作一个数组。

标签: powershell


【解决方案1】:

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

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

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

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


一个对象是像上面一样的自定义对象,但另一个对象是 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
 

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

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

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

one two
--- ---
  1   2



three
-----
    3

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

[pscustomobject] @{ one = 1; two = 2 } | Format-Table
[pscustomobject] @{ three = 3 } | Format-Table

正如Mark Wragg's blog post 解释的那样,由给定脚本生成的所有输出——即使是跨不同的命令——都被发送到同一管道。
(您可以将交互式提交的命令行视为隐式脚本。)

有关如何在显示格式方面处理单个管道中的混合类型的更详细讨论,请参阅this answer。


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

通过显式管道到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 时,您将 nothing 发送到脚本的管道。

【讨论】:

    【解决方案2】:

    试试这个:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-08
      • 2010-11-30
      • 2012-07-30
      • 2018-04-17
      • 2018-12-10
      • 1970-01-01
      相关资源
      最近更新 更多