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