company_banner

Что такое Windows PowerShell и с чем его едят? Часть 4: Работа с объектами, собственные классы



    Текстовый вывод команд в окне интерпретатора 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: передача параметров в скрипты и функции, создание командлетов

    RUVDS.com
    RUVDS – хостинг VDS/VPS серверов

    Комментарии 0

    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

    Самое читаемое