Путаница пользовательских объектов 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

Когда я запускаю это, кажется, что объекты объединены вместе. Когда я делаю 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 должен был бы выводить.

Если я переверну заказ; только первая функция выдает результат, вторая выдает только пустые строки.

Почему я не могу просто вызвать две функции в последовательности, как это?

2 ответа

Решение

Когда я делаю 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 вывод, только тот тип первого объекта определяет, какие свойства (столбцы) показывать в результирующей таблице.

Если у последующих объектов нет тех же свойств, что и у первого объекта, они просто печатают пустую строку; если они имеют одни и те же свойства, печатаются только те.

Важно отметить, что это только проблема отображения: все объекты, которые были выведены, все еще присутствуют; если вы отправите вывод другой команде для дальнейшей обработки, а не прямо на консоль, все они будут там.

Простой пример:

PS> [pscustomobject] @{ one = 1; two = 2 }, [pscustomobject] @{ three = 3 }

one two
--- ---
  1   2
 

Обратите внимание, что 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

Необязательно: почему форматирование вывода PowerShell работает так, как оно работает:

Как объясняется в блоге Марка Врэгга, весь вывод, производимый данным сценарием - даже через отдельные команды - отправляется в один и тот же конвейер.
(Вы можете думать о командной строке, представленной интерактивно, как о неявном скрипте.)

Несмотря на то, что вы можете отправлять любое соединение типов данных в конвейер, его форматирование по умолчанию оптимизировано для объектов того же типа, как это более типичный случай.

Следующее частично основано на экспериментах - скажите мне, если я что-то не так понял.

При отсутствии явных команд форматирования (Format-Table, Format-List,...), PowerShell автоматически выбирает подходящий формат отображения на основе предварительно настроенных данных форматирования данного типа (см. Get-Help about_Format.ps1xml) или, при их отсутствии, на основе простых правил (4 или меньше свойств? -> Format-Table; 5 или больше? -> Format-List).

Однако это первый объект, отправленный в конвейер, который определяет формат отображения для всех объектов:

  • В случае Format-Table выбрано, это также означает, что первый объект сам по себе определяет набор столбцов таблицы (свойств), что может привести к тому, что последующие объекты "исчезнут", если они не имеют таких же свойств, как мы видели.

    • Похоже, что единственными типами данных, на которые это не влияет, являются базовые типы.NET, такие как [int] а также [string] - они печатаются нормально, независимо от того, какие столбцы отображения были выбраны первым объектом.

    • На заметку: начиная с PSv5, неявно применяется Format-Table отображает асинхронное поведение, которое может быть удивительным; увидеть этот ответ мой.

  • В случае Format-List выбран, нет проблем, так как свойства каждого объекта перечислены индивидуально.

Опять же, обратите внимание, что даже "исчезающие" объекты являются просто проблемой отображения: объекты есть, и отправка их другой команде для дальнейшей обработки работает просто отлично.


Почему использование явной команды форматирования помогает:

Явно доставляя Format-* командлет (например, [pscustomobject] @{ one = 1; two = 2 } | Format-Table), вы на самом деле отправляете объекты форматирования (различные [Microsoft.PowerShell.Commands.Internal.Format.*] типы) в конвейер, и PowerShell затем эффективно передает их для отображения.

Альтернативой является использование общего обходного пути: если вы передаете Out-Host вместо этого (например, [pscustomobject] @{ one = 1; two = 2 } | Out-Host), в таком случае:

  • вы обходите конвейер и печатаете прямо на консоль (если вы запускаете PowerShell в обычном окне консоли),
  • и применяется форматное представление объекта по умолчанию.

Важно отметить, что эти обходные пути подходят только для целей отображения, поскольку исходные объекты теряются в процессе:

  • Когда вы трубите к Format-* Командлет явно заменяет исходный объект на объекты, содержащие инструкции по форматированию, которые бесполезны для дальнейшей обработки.

  • Когда ты трубишь в Out-Host вы ничего не отправляете в конвейер скрипта.

Попробуй это:

$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
Другие вопросы по тегам