Текстовият изход на командите в прозореца на PowerShell е просто начин за представяне на информация в разбираем за човека формат. Всъщност средата за работа с обекти: командлетите и функциите получават обектите на вход и , а наличните в интерактивния режим и в скриптовете типове променливи се основават на класовете на .NET. В четвърта статия от цикъла ще разгледаме работата с обектите по-подробно.
Съдържание:
Обекти в 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 се прилага и за работа с COM компоненти. Техният брой е значителен - от доставените с Windows Server библиотеки до приложения ActiveX, като например Internet Explorer. За да създадете COM обект, трябва да зададете параметъра -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 = 'Иван Данков';
City = 'Москва';
Country = 'Русия'}
Нека да погледнем структурата на обекта:
$object | Get-Member 
Започвайки от PowerShell 3.0, е наличен и друг синтаксис:
$object = [PSCustomObject]@{Name = 'Иван Данков';
City = 'Москва';
Country = 'Русия'
}
Доступът до данните може да се осъществи по един от следните еквивалентни начини:
$object.Name
$object.'Name'
$value = 'Name'
$object.$value
Ще дам пример за преобразуване в обект на съществуваща хеш таблица:
$hash = @{'Name'='Иван Данков'; 'City'='Москва'; 'Country'='Русия'}
$hash.GetType()
$object = [pscustomobject]$hash
$object.GetType()

Един от недостатъците на обектите от този тип е, че редът на свойствата им може да се промени. За да се избегне това, е необходимо да се използва атрибутът [ordered]:
$object = [PSCustomObject][ordered]@{Name = 'Иван Данков';
City = 'Москва';
Country = 'Русия'
}
Има и други варианти за създаване на обект: по-горе разгледахме използването на командлета . Остава да разберем как да добавим и премахнем елементи. Да го направим за обекта от предишния пример е доста просто:
$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
}
За създаване на обект (екземпляр на класа) се използва командлетът , или литералът на типа [MyClass] и new (конструктор по подразбиране):
$object = New-Object -TypeName MyClassили
$object = [MyClass]::new()Нека анализираме структурата на обекта:
$object | Get-Member 
Не трябва да забравяме за областта на видимост: не може да се прави справка към името на типа като низ или да се използва литералът на типа извън скрипта или модула, в който е дефиниран класът. В същото време функциите могат да връщат екземпляри на класа (обекти), които ще бъдат достъпни извън модула или скрипта.
След създаването на обекта, нека запълним неговите свойства:
$object.Name = 'Иван Данков'
$object.City = 'Москва'
$object.Country = 'Русия'
$object

Забележете, че в описанието на класа се задават не само типовете свойства, но и техните стойности по подразбиране:
class Example
{
[string]$Name = 'Джон До'
}
Описанието на метода на класа наподобява описанието на функция, но без използването на служебната дума 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 с външни софтуерни компоненти. Можете да намерите предишните части на връзките по-долу.
Източник: habr.com
