Текстовый вывод команд в окне интерпретатора PowerShell — всего лишь способ отображения информации в пригодном для человеческого восприятия виде. На самом деле среда ориентирована на работу с объектами: командлеты и функции получают их на входе и возвращают на выходе, а доступные в интерактивном режиме и в сценариях типы переменных базируются на классах .NET. В четвертой статье цикла мы изучим работу с объектами более детально.
Оглавление:
Объекты в PowerShell
Просмотр структуры объектов
Фильтрация объектов
Сортировка объектов
Выделение объектов и их частей
ForEach-Object, Group-Object и Measure-Object
Создание объектов .NET и COM (New-Object)
Вызов статических методов
Тип PSCustomObject
Создание собственных классов
Объекты в PowerShell
Напомним, что объект — это совокупность полей данных (свойств, событий и т.д.) и способов их обработки (методов). Его структура задается типом, который как правило базируется на использующихся в унифицированной платформе .NET Core классах. Также есть возможность работать с объектами COM, CIM (WMI) и ADSI. Свойства и методы нужны для выполнения различных действий над данными, кроме того в PowerShell объекты можно передавать как аргументы в функции и командлеты, присваивать их значения переменным, а также существует механизм композиции команд (конвейер или pipeline). Каждая команда в конвейере передает свой вывод следующей поочередно — объект за объектом. Для обработки можно использовать скомпилированные командлеты или создавать собственные расширенные функции, чтобы производить различные манипуляции с объектами в конвейере: фильтрацию, сортировку, группировку и даже изменение их структуры. Передача данных в таком виде имеет серьезное преимущество: принимающей команде не нужно заниматься синтаксическим разбором потока байтов (текста), вся нужная информация легко извлекается с помощью обращения к соответствующим свойствам и методам.
Просмотр структуры объектов
Для примера запустим командлет Get-Process, позволяющий получить информацию о работающих в системе процессах:
Он выведет на экран некие отформатированные текстовые данные, не дающие представления о свойствах возвращаемых объектов и их методах. Для тонкого препарирования вывода необходимо научиться исследовать структуру объектов и в этом нам поможет командлет Get-Member:
Get-Process | Get-Member
Здесь мы уже видим тип и структуру, а с помощью дополнительных параметров можем, например, вывести только свойства попавшего на вход объекта:
Get-Process | Get-Member -MemberType Property
Эти знания понадобятся для решения задач администрирования в интерактивном режиме или для написания собственных скриптов: скажем, чтобы получить сведения о зависших процессах по свойству Responding.
Фильтрация объектов
PowerShell позволяет пропускать по конвейеру объекты, удовлетворяющие определенному условию:
Where-Object { блок сценария }
Результатом выполнения блока сценария в операторных скобках должно быть логические значение. Если оно истинно ($true) попавший на вход командлету Where-Object объект будет передан по конвейеру дальше, в противном случае (значение $false) он будет удален. Для примера выведем список остановленных служб Windows Server, т.е. таких, у которых свойство Status имеет значение «Stopped»:
Get-Service | Where-Object {$_.Status -eq "Stopped"}
Здесь мы снова видим текстовое представление, но при желании понять тип и внутреннее устройство проходящих через конвейер объектов нетрудно:
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Get-Member
Сортировка объектов
При конвейерной обработке объектов часто возникает необходимость их сортировки. В командлет Sort-Object передаются имена свойств (ключей сортировки), а он возвращает упорядоченные по их значениям объекты. Вывод запущенных процессов несложно отсортировать по затраченному процессорному времени (свойство cpu):
Get-Process | Sort-Object –Property cpu
Параметр -Property при вызове командлета Sort-Object можно не указывать — он используется по умолчанию. Для обратной сортировки применяется параметр -Descending:
Get-Process | Sort-Object cpu -Descending
Выделение объектов и их частей
Командлет Select-Object позволяет выделить определенное количество объектов в начале или в конце конвейера с помощью параметров -First или -Last. С его помощью можно выбрать единичные объекты или определенные свойства, а также создать на их основе новые объекты. Разберем работу командлета на простых примерах.
Следующая команда выводит информацию о 10 процессах, потребляющих максимальный объем оперативной памяти (свойство WS):
Get-Process | Sort-Object WS -Descending | Select-Object -First 10
Можно выделить только определенные свойства проходящих через конвейер объектов и создать на их основе новые:
Get-Process | Select-Object ProcessName, Id -First 1
В результате работы конвейера мы получим новый объект, структура которого будет отличаться от структуры возвращаемых командлетом Get-Process. Убедимся в этом при помощи Get-Member:
Get-Process | Select-Object ProcessName, Id -First 1 | Get-Member
Обратите внимание, что Select-Object возвращает единичный объект (-First 1), у которого всего два указанных нами поля: их значения были скопированы из первого переданного в конвейер командлетом Get-Process объекта. На использовании Select-Object основан один из способов создания объектов в сценариях PowerShell:
$obj = Get-Process | Select-Object ProcessName, Id -First 1
$obj.GetType()
С помощью Select-Object можно добавлять объектам вычисляемые свойства, которые необходимо представить в виде хэш-таблицы. При этом значение ее первого ключа соответствует имени свойства, а значение второго — значению свойства для текущего элемента конвейера:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}}
Посмотрим на структуру проходящих через конвейер объектов:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} | Get-Member
ForEach-Object, Group-Object и Measure-Object
Для работы с объектами существуют и другие командлеты. Для примера расскажем о трех наиболее полезных:
ForEach-Object позволяет выполнить код на языке PowerShell для каждого объекта в конвейере:
ForEach-Object { блок сценария }
Group-Object группирует объекты по значению свойства:
Group-Object PropertyName
Если запустить его с параметром -NoElement, можно узнать количество элементов в группах.
Measure-Object агрегирует различные сводные параметры по значениям полей объектов в конвейере (вычисляет сумму, а также находит минимальное, максимальное или среднее значение):
Measure-Object -Property PropertyName -Minimum -Maximum -Average -Sum
Обычно рассмотренные командлеты используются в интерактивном режиме, а в скриптах чаще создаются функции с блоками Begin, Process и End.
Создание объектов .NET и COM (New-Object)
Есть множество программных компонентов с интерфейсами .NET Core и COM, которые пригодятся системным администраторам. С помощью класса System.Diagnostics.EventLog можно управлять системными журналами непосредственно из Windows PowerShell. Разберем пример создания экземпляра этого класса при помощи командлета New-Object с параметром -TypeName:
New-Object -TypeName System.Diagnostics.EventLog
Поскольку мы не указали определенный журнал событий, полученный экземпляр класса не содержит данных. Чтобы это изменить, необходимо во время его создания вызвать специальный метод-конструктор при помощи параметра -ArgumentList. Если мы хотим получить доступ к журналу приложений, в конструктор следует передать строку «Application» в качестве аргумента:
$AppLog = New-Object -TypeName System.Diagnostics.EventLog -ArgumentList Application
$AppLog
Обратите внимание: выходные данные команды мы сохранили в переменной $AppLog. Хотя в интерактивном режиме обычно используются конвейеры, написание сценариев часто требует сохранения ссылки на объект. Кроме того основные классы .NET Core содержатся в пространстве имен System: PowerShell по умолчанию ищет в нем указанные типы, поэтому написание Diagnostics.EventLog вместо System.Diagnostics.EventLog вполне корректно.
Для работы с журналом можно обращаться к соответствующим методам:
$AppLog | Get-Member -MemberType Method
Скажем очищается он методом Clear() при наличии прав доступа:
$AppLog.Clear()
Командлет New-Object применяется и для работы с СОМ-компонентами. Их довольно много — от поставляемых с сервером сценариев Windows библиотек до приложений ActiveX, таких, например, как Internet Explorer. Чтобы создать СОМ-объект, требуется задать параметр -ComObject с программным идентификатора ProgId нужного класса:
New-Object -ComObject WScript.Shell
New-Object -ComObject WScript.Network
New-Object -ComObject Scripting.Dictionary
New-Object -ComObject Scripting.FileSystemObject
Для создания собственных объектов с произвольной структурой использование New-Object выглядит слишком архаичным и громоздким, этот командлет используется для работы с внешними по отношению к PowerShell программными компонентами. В следующих статьях этот вопрос будет разобран более подробно. Помимо объектов .NET и COM мы также изучим объекты CIM (WMI) и ADSI.
Вызов статических методов
Экземпляры некоторых классов .NET Core создать невозможно: к их числу относятся System.Environment и System.Math. Они являются статическими и содержат только статические свойства и методы. По сути это справочные библиотеки, которые используются без создания объектов. Сослаться на статический класс можно через литерал, заключив имя типа в квадратные скобки. При этом если посмотреть на структуру объекта с помощью Get-Member, мы увидим тип System.RuntimeType вместо System.Environment:
[System.Environment] | Get-Member
Для просмотра только статических элементов нужно вызвать Get-Member с параметром -Static (обратите внимание на тип объекта):
[System.Environment] | Get-Member -Static
Для доступа к статическим свойствам и методам используются два идущих подряд двоеточия вместо точки после литерала:
[System.Environment]::OSVersion
Или
$test=[System.Math]::Sqrt(25)
$test
$test.GetType()
Тип PSCustomObject
Среди многочисленных доступных в PowerShell типов данных отдельно стоит упомянуть PSCustomObject, предназначенный для хранения объектов с произвольной структурой. Создание такого объекта с помощью командлета New-Object считается классическим, но громоздким и устаревшим способом:
$object = New-Object –TypeName PSCustomObject -Property @{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'}
Посмотрим на структуру объекта:
$object | Get-Member
Начиная с PowerShell 3.0 доступен и другой синтаксис:
$object = [PSCustomObject]@{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'
}
Получить доступ к данным можно одним из эквивалентных способов:
$object.Name
$object.'Name'
$value = 'Name'
$object.$value
Приведем пример преобразования в объект существующей хэштаблицы:
$hash = @{'Name'='Ivan Danko'; 'City'='Moscow'; 'Country'='Russia'}
$hash.GetType()
$object = [pscustomobject]$hash
$object.GetType()
Один из недостатков объектов этого типа — порядок их свойств может поменяться. Чтобы этого избежать, необходимо использовать атрибут [ordered]:
$object = [PSCustomObject][ordered]@{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'
}
Есть и другие варианты создания объекта: выше мы рассмотрели использование командлета Select-Object. Осталось разобраться с добавлением и удалением элементов. Сделать это для объекта из предыдущего примера довольно просто:
$object | Add-Member –MemberType NoteProperty –Name Age –Value 33
$object | Get-Member
Командлет Add-Member позволяет добавлять ранее созданному объекту $object не только свойства, но и методы посредством использования конструкции "-MemberType ScriptMethod":
$ScriptBlock = {
# код
}
$object | Add-Member -Name "MyMethod" -MemberType ScriptMethod -Value $ScriptBlock
$object | Get-Member
Обратите внимание: для хранения кода нового метода мы использовали переменную $ScriptBlock типа ScriptBlock.
Для удаления свойств используется соответствующий метод:
$object.psobject.properties.remove('Name')
Создание собственных классов
В PowerShell 5.0 появилась возможность определения классов с использованием характерного для объектно-ориентированных языков программирования синтаксиса. Для этого предназначено служебное слово Class, после которого следует задать имя класса и описать его тело в операторных скобках:
class MyClass
{
# тело класса
}
Это настоящий тип .NET Core, в теле которого описываются его свойства, методы и другие элементы. Рассмотрим пример определения простейшего класса:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
}
Для создания объекта (экземпляра класса) используется командлет New-Object, либо литерал типа [MyClass] и псевдостатический метод new (конструктор по умолчанию):
$object = New-Object -TypeName MyClass
или
$object = [MyClass]::new()
Проанализируем структуру объекта:
$object | Get-Member
Не стоит забывать про область видимости: нельзя ссылаться на имя типа в виде строки или использовать литерал типа за пределами скрипта или модуля, в котором определен класс. При этом функции могут возвращать экземпляры класса (объекты), которые будут доступны вне модуля или скрипта.
После создания объекта заполним его свойства:
$object.Name = 'Ivan Danko'
$object.City = 'Moscow'
$object.Country = 'Russia'
$object
Отметим, что в описании класса задаются не только типы свойств, но и их значения по умолчанию:
class Example
{
[string]$Name = 'John Doe'
}
Описание метода класса напоминает описание функции, но без использования служебного слова function. Как и в функции, в методы при необходимости передаются параметры:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
#описание метода
Smile([bool]$param1)
{
If($param1) {
Write-Host ':)'
}
}
}
Теперь представитель нашего класса умеет улыбаться:
$object = [MyClass]::new()
$object.Smile($true)
Методы можно перегружать, кроме того у класса бывают статические свойства и методы, а также конструкторы, имена которых совпадают с именем самого класса. Определенный в скрипте или модуле PowerShell класс может служить базовым для другого — так реализуется наследование. При этом в качестве базовых допускается использование существующих классов .NET:
class MyClass2 : MyClass
{
#тело нового класса, базовым для которого является MyClass
}
[MyClass2]::new().Smile($true)
Наше описание работы с объектами в PowerShell трудно назвать исчерпывающем. В следующих публикациях попробуем его углубить на практических примерах: пятая статья цикла будет посвящена вопросам интеграции PowerShell со сторонними программными компонентами. Прошлые части можно найти по ссылкам ниже.
Часть 1: основные возможности Windows PowerShell
Часть 2: введение в язык программирования Windows PowerShell
Часть 3: передача параметров в скрипты и функции, создание командлетов