Командлет Format-Wide служит для отображения какого-либо одного свойства объектов, поступаемых к нему по конвейеру, либо указанных в качестве значения параметра -InputObject.
Например, так:
Get-Process | Format-Wide
или так:
Format-Wide -InputObject (Get-Process)
В обоих случаях мы увидим несколько колонок, составленных из значений свойств ProcessName объектов System.Diagnostics.Process.
.format.ps1xml
Так как мы не указали какое-либо конкретное свойство, то командлет Format-Wide поступил так, как и полагается форматирующему командлету и посмотрел в файл DotNetTypes.format.ps1xml, расположенный в C:\Windows\System32\WindowsPowerShell\v1.0 для того чтобы осведомиться, нет ли так каких либо уже определенных представлений (view) для объектов System.Diagnostics.Process. Там это представление было и оно же и указало, что при отсутствии заданного параметра -Property нужно выводить значения свойства ProcessName.
<View> <Name>process</Name> <ViewSelectedBy> <TypeName>System.Diagnostics.Process</TypeName> </ViewSelectedBy> <WideControl> <WideEntries> <WideEntry> <WideItem> <PropertyName>ProcessName</PropertyName> </WideItem> </WideEntry> </WideEntries> </WideControl> </View>
Мы также можем узнать о существовании представлений для данных определенного типа данных не забираясь в дебри xml. Для этого нам нужно указать параметр -View, но в качестве его значения задать что-то, что бы точно не пришло в голову разработчикам командлетов, когда они думали о том, как бы назвать определенное представление. Например, так:
Get-Process | Format-Wide -View Some_Not_Very_Common_View_Name
Результатом выполнения этой команды станет сообщение об ошибке, в котором нам пояснят, что представления Some_Not_Very_Common_View_Name не существует, но есть такое: process.
Format-Wide : The view name Some_Not_Very_Common_View_Name cannot be found. Specify one of the following Wide views and try again: process.
Что же это за представление, мы можем узнать, введя команду:
Get-FormatData -TypeName System.Diagnostics.Process
В ответ мы получим что-то вроде:
TypeNames FormatViewDefinition --------- -------------------- {System.Diagnostics.Process} {process, process}
где нас определенно интересует, что же это за два загадочных представления в столбце FormatViewDefinition. Развернуть его можно несколькими способами. Например, так:
Get-FormatData -TypeName System.Diagnostics.Process | Select-Object -ExpandProperty FormatViewDefinition
или так:
Get-FormatData -TypeName System.Diagnostics.Process | ForEach-Object FormatViewDefinition
Заменив ForEach-Object его алиасом %, мы получим еще более компактную запись:
Get-FormatData -TypeName System.Diagnostics.Process | % FormatViewDefinition
Однако, продолжим. Результатом выполнения любой из трех вышеуказанных команд будет являться что-то вроде:
Name Control ---- ------- process System.Management.Automation.TableControl process System.Management.Automation.WideControl
Как мы видим, для объектов типа System.Diagnostics.Process существует два представления, а именно: табличное представление, которое будет использоваться по умолчанию командлетом Format-Table, и то, которое нас сейчас интересует — для командлета Format-Wide. Тот факт, что оба этих представления фигурируют под одним и тем же именем — process — в данном случае не представляет каких-либо неудобств, так как это представления разного типа, использующиеся разными командлетами.
Попробуем закопаться чуть глубже. Нам нужно только второе представление, поэтому сделаем так, чтобы в дальнейшей работе использовалось только оно:
Get-FormatData -TypeName System.Diagnostics.Process | % FormatViewDefinition | select -Last 1
Мы получим следующий результат:
Name Control ---- ------- process System.Management.Automation.WideControl
Уже знакомым способом получим содержимое свойства Control:
Get-FormatData -TypeName System.Diagnostics.Process | % FormatViewDefinition | select -Last 1 | % Control
Мы увидим:
Alignment : 0 Entries : {System.Management.Automation.WideControlEntryItem} AutoSize : False Columns : 0 GroupBy : OutOfBand : False
И что нас тут более всего интересует, так это значение свойства Entries:
Get-FormatData -TypeName System.Diagnostics.Process | % FormatViewDefinition | select -Last 1 | % Control | % Entries
И тут мы увидим следующее:
DisplayEntry SelectedBy EntrySelectedBy FormatString ------------ ---------- --------------- ------------ property: ProcessName {} System.Management.Automation.EntrySelectedBy
где в первом столбце указано искомое свойство:
property: ProcessName
.types.ps1xml
В случае, если для определенного типа данных не указно представление, которое мог бы использовать форматирующий командлет в отсутствие заданого параметра -Property, он заглядывает еще в одно место — в файлы описания типов.
На этот раз давайте приглядимся IPv6-адресам, а точнее к типу Microsoft.DnsClient.Commands.DnsRecord_AAAA. Объект данного типа мы можем получить попытавшись разрешить какое-либо доменное имя в IPv6-адрес, например:
Resolve-DnsName google.com -Type AAAA
Мы получим что-то вроде:
Name Type TTL Section IPAddress ---- ---- --- ------- --------- google.com AAAA 206 Answer 2a00:1450:4010:c08::8a
Теперь, если мы передадим результат выполнения Resolve-DnsName командлету Format-Wide без указания параметра -Property, мы получим:
Resolve-DnsName google.com -Type AAAA | Format-Wide
2a00:1450:4010:c06::8b
Почему именно свойство IP6Address?
Мы знаем, что в отсутствие заданного параметра -Property форматирующие командлеты смотрят, определено ли какое-либо представление для выводимого типа данных.
Get-FormatData -TypeName Microsoft.DnsClient.Commands.DnsRecord_AAAA
TypeNames FormatViewDefinition --------- -------------------- {Microsoft.DnsClient.Commands.DnsRecord_A} {Microsoft.DnsClient.Commands.DnsRecord_A_AAAA, Microsoft.DnsClient.Commands.DnsRecord_A_AAAA} {Microsoft.DnsClient.Commands.DnsRecord_AAAA} {Microsoft.DnsClient.Commands.DnsRecord_A_AAAA, Microsoft.DnsClient.Commands.DnsRecord_A_AAAA}
Отбираем второй объект — Microsoft.DnsClient.Commands.DnsRecord_AAAA — и раскрываем содержимое свойства FormatViewDefinition:
Get-FormatData -TypeName Microsoft.DnsClient.Commands.DnsRecord_AAAA | select -last 1 | % formatviewdefinition
Получаем:
Name Control ---- ------- Microsoft.DnsClient.Commands.DnsRecord_A_AAAA System.Management.Automation.TableControl Microsoft.DnsClient.Commands.DnsRecord_A_AAAA System.Management.Automation.ListControl
Как мы видим из результата выполнения команды, для типа данных Microsoft.DnsClient.Commands.DnsRecord_AAAA существуют представления только для табличного (TableControl) и списочного (ListControl) вида, а представление типа WideControl для командлета Format-Wide — отсутствует.
Теперь завайте заглянем в слудеющее место, где форматирующий командлет будет искать информацию о параметрах вывода объекта определенного типа. В нашем случае — это файл DnsCmdlets.Types.ps1xml, находящийся в каталоге C:\Windows\System32\WindowsPowerShell\v1.0\Modules\DnsClient.
Заглянув в него, мы можем найти что-то вроде:
<Type> <Name>Microsoft.DnsClient.Commands.DnsRecord_AAAA</Name> <Members> <AliasProperty> <Name>Address</Name> <ReferencedMemberName>IP6Address</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>IPAddress</Name> <ReferencedMemberName>IP6Address</ReferencedMemberName> </AliasProperty> <AliasProperty> <Name>QueryType</Name> <ReferencedMemberName>Type</ReferencedMemberName> </AliasProperty> <MemberSet> <Name>PsStandardMembers</Name> <Members> <PropertySet> <Name>DefaultDisplayPropertySet</Name> <ReferencedProperties> <Name>Name</Name> <Name>QueryType</Name> <Name>TTL</Name> <Name>Section</Name> <Name>IP6Address</Name> </ReferencedProperties> </PropertySet> <NoteProperty> <Name>DefaultDisplayProperty</Name> <Value>IP6Address</Value> </NoteProperty> <PropertySet> <Name>DefaultKeyPropertySet</Name> <ReferencedProperties> <Name>IP6Address</Name> </ReferencedProperties> </PropertySet> </Members> </MemberSet> </Members> </Type>
Эта структура определяет дополнительные свойства типа Microsoft.DnsClient.Commands.DnsRecord_AAAA, а также некоторую другую информацию. Нас же в данный момент интересуют выделенные строки. Как мы видим, здесь в качестве DefaultDisplayProperty указан IP6Address, что вполне сходится с тем, что мы получаем в качестве вывода.
Стоит сказать, что это значение действительно только в том случае, если для выводимого типа данных не определено ни одного представления соответствующего типа форматирования (Table, List, Wide, Custom). В противном случае DefaultDisplayProperty (для Format-Wide), равно как и DefaultDisplayPropertySet (для Format-Table, Format-List и Format-Custom) будут игнорироваться и использоваться будет именно представление.
Format-Wide
Однако у командлета Format-Wide есть еще одна интересная особенность — это использование определенных свойств в случае, если не задано значение параметра -Property, не существует каких-либо представлений и не определено значение DefaultDisplayProperty.
Например:
Get-Service | Format-Wide
Результатом этой команды будет вывод значений свойств Name объектов System.ServiceProcess.ServiceController. Однако, введя команду:
Get-Service | Format-Wide -View abcdefghijklm
мы получим:
Format-Wide : The view name abcdefghijklm cannot be found. There are no existing Wide views for System.ServiceProcess.ServiceController objects.
Текст ошибки, полученный в результате выполнения предыдущей команды, сообщает, что для типа System.ServiceProcess.ServiceController вообще не существует представлений, что могли бы использоваться командлетом Format-Wide.
Точно так же, следующая команда:
Get-Service | Get-Member -MemberType MemberSet -Force -Name PSStandardMembers
выведет что-то вроде:
TypeName: System.ServiceProcess.ServiceController Name MemberType Definition ---- ---------- ---------- PSStandardMembers MemberSet PSStandardMembers {DefaultDisplayPropertySet}
Правая часть последней строки говорит нам о том, что для объекта службы определено только значение DefaultDisplayPropertySet, что используется командлетами Format-Table, Format-List и Format-Custom. Значения DefaultDisplayProperty, используемого командлетом Format-Wide, не существует (равно как и DefaultKeyPropertySet).
Возвращаясь в примеру с записями DNS, для типа Microsoft.DnsClient.Commands.DnsRecord_AAAA:
Resolve-DnsName google.com -Type AAAA | Get-Member -MemberType MemberSet -Force -Name PSStandardMembers
мы видим:
TypeName: Microsoft.DnsClient.Commands.DnsRecord_AAAA Name MemberType Definition ---- ---------- ---------- PSStandardMembers MemberSet PSStandardMembers {DefaultDisplayProperty, DefaultDisplayPropertySet, DefaultKeyPropertySet}
что для типа Microsoft.DnsClient.Commands.DnsRecord_AAAA определены как DefaultDisplayProperty, так и DefaultDisplayPropertySet и DefaultKeyPropertySet.
Так почему же для объекта службы командлет Format-Wide вывел свойство Name?
Получается так, что в отсутствие каких-либо указаний, что посредством параметра -Property, что через файлы .format.ps1xml или types.ps1xml, командет Format-Wide выбирает свойства Name или ID, а также свойства, имена которых заканчиваются на name или ID. Причем Name приоритетнее, чем ID, а при существовании и Name и чего_нибудь_Name, опять же выбирается Name, что мы и видели в примере со службами, где присутствует и Name, и DisplayName, но выводится именно Name.
Проще это проверить, создав свой объект, для которого точно не будет определено каких-либо представлений и расширений типов:
$hash1 = @{ Description = "This is first object's description"; SomeID = "First Object's SomeID"; Some_Other_Data = "This is Some Other Data" } $hash2 = @{ Description = "This is second object's description"; SomeID = "Second Object's SomeID"; Some_Other_Data = "This is Some Other Data" } $objects = @(New-Object -TypeName PSCustomObject -Property $hash1) $objects += New-Object -TypeName PSCustomObject -Property $hash2 $objects | Format-Wide
Сначала о том, что мы тут делаем. В первой части мы создаем хэш-таблицу, на основе которой и будем строить объект. В ней содержатся имена свойств и их значения. Как видно, из интересующих нас свойств мы определили только SomeID. Далее мы создаем хэш-таблицу для второго объекта с чуть измененными значениями свойств.
После этого мы создаем новый объект типа PSCustomObject используя имена и значения нашей первой хэш-таблицы и сохраняем его в переменную $objects. Причем мы сразу же задаем переменную $objects в виде массива, что пригодится нам, когда мы будем добавлять в нее второй объект. Делаем мы это при помощи конструкции @().
Далее мы добавляем к массиву в переменной $objects второй объект и передаем ее содержимое командлету Format-Wide.
Что мы получим в итоге, это значение свойств SomeID:
First Object's SomeID Second Object's SomeID
Теперь давайте к описанию объектов добавим свойство DisplayName:
$hash1 = @{ Description = "This is first object's description"; SomeID = "First Object's SomeID"; Some_Other_Data = "This is Some Other Data"; DisplayName = "First Object's DisplayName" } $hash2 = @{ Description = "This is second object's description"; SomeID = "Second Object's SomeID"; Some_Other_Data = "This is Some Other Data"; DisplayName = "Second Object's DisplayName" } $objects = @(New-Object -TypeName PSCustomObject -Property $hash1) $objects += New-Object -TypeName PSCustomObject -Property $hash2 $objects | Format-Wide
Теперь в качестве результатов выполнения командлета Format-Wide мы получим значения свойств DisplayName:
First Object's DisplayName Second Object's DisplayName
Продолжая наши изыскания, добавим ID:
$hash1 = @{ Description = "This is first object's description"; SomeID = "First Object's SomeID"; Some_Other_Data = "This is Some Other Data"; DisplayName = "First Object's DisplayName"; ID = "First Object's ID" } $hash2 = @{ Description = "This is second object's description"; SomeID = "Second Object's SomeID"; Some_Other_Data = "This is Some Other Data"; DisplayName = "Second Object's DisplayName"; ID = "Second Object's ID" } $objects = @(New-Object -TypeName PSCustomObject -Property $hash1) $objects += New-Object -TypeName PSCustomObject -Property $hash2 $objects | Format-Wide
В качестве результата видим значение свойства ID:
First Object's ID Second Object's ID
Теперь добавим свойство Name:
$hash1 = @{ Description = "This is first object's description"; SomeID = "First Object's SomeID"; Some_Other_Data = "This is Some Other Data"; DisplayName = "First Object's DisplayName"; ID = "First Object's ID"; Name = "First Object" } $hash2 = @{ Description = "This is second object's description"; SomeID = "Second Object's SomeID"; Some_Other_Data = "This is Some Other Data"; DisplayName = "Second Object's DisplayName"; ID = "Second Object's ID"; Name = "Second Object" } $objects = @(New-Object -TypeName PSCustomObject -Property $hash1) $objects += New-Object -TypeName PSCustomObject -Property $hash2 $objects | Format-Wide
И в качестве вывода получаем его значение:
First Object Second Object
Что, собственно, и ожидалось.
Страницы в социальных сетях:
Twitter: https://twitter.com/vsseth
Facebook: https://fb.com/inpowershell
VKontakte: https://vk.com/inpowershell