Tekstowy output poleceń w oknie interpretera PowerShell to jedynie sposób na wyświetlanie informacji w formie zrozumiałej dla człowieka. W rzeczywistości środowisko na pracę z obiektami: cmdlety i funkcje otrzymują je jako wejście i , a dostępne w trybie interaktywnym i w skryptach typy zmiennych opierają się na klasach .NET. W czwartej części cyklu dokładniej przyjrzymy się pracy z obiektami.
Spis treści:
Obiekty w PowerShell
Przypomnijmy, że obiekt to zbiór pól danych (właściwości, zdarzenia itd.) oraz metod ich przetwarzania. Jego struktura ustalana jest przez typ, który zazwyczaj opiera się na klasach używanych w zunifikowanej platformie .NET Core. Istnieje również możliwość pracy z obiektami COM, CIM (WMI) i ADSI. Właściwości i metody są potrzebne do wykonywania różnych działań na danych, ponadto w PowerShell obiekty można przekazywać jako argumenty do funkcji i cmdletów, przypisywać ich wartości do zmiennych, a także istnieje (rura lub pipeline). Każde polecenie w rurze przekazuje swoje wyjście do następnego jedna po drugim — obiekt po obiekcie. Do przetwarzania można używać skompilowanych cmdletów lub tworzyć własne , aby przeprowadzać różne manipulacje obiektami w rurze: filtrowanie, sortowanie, grupowanie, a nawet modyfikowanie ich struktury. Przekazywanie danych w ten sposób ma poważną zaletę: przyjmującemu poleceniu nie trzeba zajmować się analizowaniem strumienia bajtów (tekstu), wszystkie potrzebne informacje są łatwo wyciągane przy użyciu odpowiednich właściwości i metod.
Przegląd struktury obiektów
Na przykład uruchomimy cmdlet Get-Process, który pozwala uzyskać informacje o działających w systemie procesach:

Wyświetli on na ekranie sformatowane dane tekstowe, które nie dają wyobrażenia o właściwościach zwracanych obiektów i ich metodach. Aby szczegółowo analizować wynik, trzeba nauczyć się badać strukturę obiektów, a w tym pomoże nam polecenie Get-Member:
Get-Process | Get-Member 
Tutaj widzimy już typ i strukturę, a za pomocą dodatkowych parametrów możemy na przykład wyświetlić tylko właściwości obiektu, który trafił na wejście:
Get-Process | Get-Member -MemberType PropertyTe informacje będą potrzebne do rozwiązywania zadań administracyjnych w trybie interaktywnym lub do pisania własnych skryptów: powiedzmy, aby uzyskać informacje o zawieszonych procesach na podstawie właściwości Responding.
Filtrowanie obiektów
PowerShell pozwala przesyłać przez potok obiekty spełniające określony warunek:
Where-Object { blok skryptu }Wynikiem wykonania bloku skryptu w nawiasach klamrowych powinno być wartość logiczna. Jeśli jest prawdziwa ($true), obiekt trafiający na wejście polecenia Where-Object zostanie przesłany dalej przez potok; w przeciwnym razie (wartość $false) zostanie usunięty. Na przykład wyświetlimy listę zatrzymanych usług Windows Server, czyli takich, których właściwość Status ma wartość „Stopped”:
Get-Service | Where-Object {$_.Status -eq "Stopped"} 
Tutaj znów widzimy tekstową reprezentację, ale jeśli chcemy zrozumieć typ i wewnętrzną strukturę obiektów przechodzących przez potok, nie jest to trudne:
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Get-Member 
Sortowanie obiektów
Podczas przetwarzania obiektów w potoku często zachodzi potrzeba ich sortowania. Do polecenia Sort-Object przekazywane są nazwy właściwości (kluczy sortowania), a on zwraca uporządkowane obiekty według ich wartości. Wyświetlenie uruchomionych procesów można łatwo posortować według wykorzystania czasu procesora (właściwość cpu):
Get-Process | Sort-Object –Property cpuParametru -Property przy wywołaniu polecenia Sort-Object nie trzeba podawać — jest on używany domyślnie. Do sortowania malejącego stosuje się parametr -Descending:
Get-Process | Sort-Object cpu -Descending 
Wydobywanie obiektów i ich części
Polecenie Select-Object pozwala wyodrębnić określoną liczbę obiektów na początku lub na końcu potoku za pomocą parametrów -First lub -Last. Dzięki niemu można wybierać pojedyncze obiekty lub określone właściwości oraz tworzyć nowe obiekty na ich podstawie. Omówmy działanie tego polecenia na prostych przykładach.
Następne polecenie wyświetli informacje o 10 procesach zużywających największą ilość pamięci operacyjnej (właściwość WS):
Get-Process | Sort-Object WS -Descending | Select-Object -First 10 
Można wybrać tylko określone właściwości obiektów przepływających przez potok i na ich podstawie utworzyć nowe:
Get-Process | Select-Object ProcessName, Id -First 1W wyniku działania potoku otrzymamy nowy obiekt, którego struktura będzie różniła się od struktury zwracanej przez polecenie Get-Process. Upewnijmy się o tym za pomocą Get-Member:
Get-Process | Select-Object ProcessName, Id -First 1 | Get-Member 
Zauważ, że Select-Object zwraca pojedynczy obiekt (-First 1), który ma tylko dwa wskazane przez nas pola: ich wartości zostały skopiowane z pierwszego obiektu przekazanego do potoku przez polecenie Get-Process. Na wykorzystaniu Select-Object opiera się jeden ze sposobów tworzenia obiektów w skryptach PowerShell:
$obj = Get-Process | Select-Object ProcessName, Id -First 1
$obj.GetType() 
Za pomocą Select-Object można dodawać do obiektów właściwości obliczeniowe, które należy zaprezentować w postaci . Wartość jej pierwszego klucza odpowiada nazwie właściwości, a wartość drugiego — wartości właściwości dla bieżącego elementu potoku:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} 
Spójrzmy na strukturę obiektów przepływających przez potok:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} | Get-Member 
ForEach-Object, Group-Object i Measure-Object
Do pracy z obiektami istnieją również inne polecenia. Dla przykładu opowiemy o trzech najprzydatniejszych:
ForEach-Object pozwala wykonać kod w języku PowerShell dla każdego obiektu w potoku:
ForEach-Object { blok skryptu }Group-Object grupuje obiekty według wartości właściwości:
Group-Object PropertyNameJeśli uruchomi się go z parametrem -NoElement, można dowiedzieć się, ile elementów jest w grupach.
Measure-Object agreguje różne podsumowujące parametry według wartości pól obiektów w potoku (oblicza sumę, a także znajduje minimalną, maksymalną lub średnią wartość):
Measure-Object -Property PropertyName -Minimum -Maximum -Average -SumZazwyczaj omówione polecenia są używane w trybie interaktywnym, a w skryptach częściej tworzy się z blokami Begin, Process i End.
Tworzenie obiektów .NET i COM (New-Object)
Istnieje wiele komponentów oprogramowania z interfejsami .NET Core i COM, które mogą być przydatne dla administratorów systemów. Dzięki klasie System.Diagnostics.EventLog można zarządzać dziennikami systemowymi bezpośrednio z Windows PowerShell. Przyjrzyjmy się przykładowi tworzenia instancji tej klasy za pomocą polecenia New-Object z parametrem -TypeName:
New-Object -TypeName System.Diagnostics.EventLog 
Ponieważ nie określiliśmy konkretnego dziennika zdarzeń, uzyskana instancja klasy nie zawiera danych. Aby to zmienić, należy podczas jej tworzenia wywołać specjalną metodę konstrukcyjną za pomocą parametru -ArgumentList. Jeśli chcemy uzyskać dostęp do dziennika aplikacji, jako argument należy przekazać ciąg „Application”:
$AppLog = New-Object -TypeName System.Diagnostics.EventLog -ArgumentList Application
$AppLog 
Zauważ, że dane wyjściowe polecenia zapisaliśmy w zmiennej $AppLog. Chociaż w trybie interaktywnym zazwyczaj używa się potoków, pisanie skryptów często wymaga zachowania odnośnika do obiektu. Dodatkowo podstawowe klasy .NET Core znajdują się w przestrzeni nazw System: PowerShell domyślnie poszukuje określonych typów w tej przestrzeni, dlatego zapisanie Diagnostics.EventLog zamiast System.Diagnostics.EventLog jest jak najbardziej poprawne.
Aby pracować z dziennikiem, można korzystać z odpowiednich metod:
$AppLog | Get-Member -MemberType Method 
Na przykład, można go wyczyścić za pomocą metody Clear(), jeśli istnieją odpowiednie uprawnienia:
$AppLog.Clear()Polecenie New-Object jest również stosowane do pracy z komponentami COM. Jest ich dość dużo — od bibliotek skryptów Windows dostarczanych z serwerem do aplikacji ActiveX, takich jak Internet Explorer. Aby utworzyć obiekt COM, należy podać parametr -ComObject z identyfikatorem programowym ProgId wymaganego klasy:
New-Object -ComObject WScript.Shell
New-Object -ComObject WScript.Network
New-Object -ComObject Scripting.Dictionary
New-Object -ComObject Scripting.FileSystemObjectDo tworzenia własnych obiektów o dowolnej strukturze użycie New-Object wygląda zbyt archaicznie i nieefektywnie; to polecenie jest wykorzystywane do pracy z zewnętrznymi względem PowerShell komponentami oprogramowania. W kolejnych artykułach zagadnienie to zostanie szczegółowo omówione. Poza obiektami .NET i COM omówimy również obiekty CIM (WMI) i ADSI.
Wywoływanie metod statycznych
Instancje niektórych klas .NET Core nie mogą być tworzone: do ich liczby należy System.Environment i System.Math. Są one i zawierają tylko statyczne właściwości i metody. W istocie są to biblioteki referencyjne, które są używane bez tworzenia obiektów. Można odwołać się do statycznej klasy poprzez literał, umieszczając nazwę typu w nawiasach kwadratowych. W takim przypadku, jeśli spojrzymy na strukturę obiektu za pomocą Get-Member, zobaczymy typ System.RuntimeType zamiast System.Environment:
[System.Environment] | Get-Member 
Aby zobaczyć tylko statyczne elementy, należy wywołać Get-Member z parametrem -Static (zauważ typ obiektu):
[System.Environment] | Get-Member -Static 
Aby uzyskać dostęp do statycznych właściwości i metod, używa się dwóch następujących po sobie dwukropków zamiast kropki po literału:
[System.Environment]::OSVersionLub
$test=[System.Math]::Sqrt(25)
$test
$test.GetType() 
Typ PSCustomObject
Spośród licznych typów danych dostępnych w PowerShell warto wyróżnić PSCustomObject, przeznaczony do przechowywania obiektów o dowolnej strukturze. Tworzenie takiego obiektu za pomocą polecenia New-Object uważane jest za klasyczną, ale przestarzałą metodę:
$object = New-Object –TypeName PSCustomObject -Property @{Name = 'Ivan Danko';
City = 'Moskwa';
Country = 'Rosja'}
Przyjrzyjmy się strukturze obiektu:
$object | Get-Member 
Począwszy od PowerShell 3.0 dostępna jest również inna składnia:
$object = [PSCustomObject]@{Name = 'Ivan Danko';
City = 'Moskwa';
Country = 'Rosja'
}
Dostęp do danych można uzyskać na jeden z równoważnych sposobów:
$object.Name
$object.'Name'
$value = 'Name'
$object.$value
Podajmy przykład konwersji istniejącej tablicy haszowanej na obiekt:
$hash = @{'Name'='Ivan Danko'; 'City'='Moskwa'; 'Country'='Rosja'}
$hash.GetType()
$object = [pscustomobject]$hash
$object.GetType()

Jedną z wad obiektów tego typu jest możliwość zmiany porządku ich właściwości. Aby tego uniknąć, należy użyć atrybutu [ordered]:
$object = [PSCustomObject][ordered]@{Name = 'Ivan Danko';
City = 'Moskwa';
Country = 'Rosja'
}
Są także inne sposoby tworzenia obiektu: wyżej omówiliśmy użycie polecenia . Pozostało rozwiązać problem dodawania i usuwania elementów. Zrobienie tego dla obiektu z poprzedniego przykładu jest dość proste:
$object | Add-Member –MemberType NoteProperty –Name Age –Value 33
$object | Get-Member

Polecenie Add-Member pozwala dodawać wcześniej stworzonymu obiektowi $object nie tylko właściwości, ale i metody za pomocą konstrukcji "-MemberType ScriptMethod:"
$ScriptBlock = {
# kod
}
$object | Add-Member -Name "MyMethod" -MemberType ScriptMethod -Value $ScriptBlock
$object | Get-Member
Zauważ, że do przechowywania kodu nowej metody użyliśmy zmiennej $ScriptBlock typu ScriptBlock.

Aby usunąć właściwości, używa się odpowiedniej metody:
$object.psobject.properties.remove('Name')
Tworzenie własnych klas
W PowerShell 5.0 wprowadzono możliwość definiowania z użyciem składni typowej dla języków programowania zorientowanych obiektowo. W tym celu używa się słowa kluczowego Class, po którym należy podać nazwę klasy i opisać jej ciało w nawiasach klamrowych:
class MyClass
{
# ciało klasy
}
Jest to rzeczywisty typ .NET Core, w którego ciele opisuje się jego właściwości, metody i inne elementy. Rozważmy przykład definiowania najprostszego klasy:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
}
Aby stworzyć obiekt (instancję klasy), używa się polecenia , lub literału typu [MyClass] oraz new (domyślny konstruktor):
$object = New-Object -TypeName MyClasslub
$object = [MyClass]::new()Przeanalizujmy strukturę obiektu:
$object | Get-Member 
Nie zapominajmy o zasięgu: nie można odnosić się do nazwy typu jako ciągu znaków ani używać literału typu poza skryptem lub modułem, w którym zdefiniowano klasę. Funkcje mogą natomiast zwracać instancje klasy (obiekty), które będą dostępne poza modułem lub skryptem.
Po utworzeniu obiektu wypełnijmy jego właściwości:
$object.Name = 'Ivan Danko'
$object.City = 'Moskwa'
$object.Country = 'Rosja'
$object

Zauważmy, że w opisie klasy określane są nie tylko typy właściwości, ale także ich wartości domyślne:
class Example
{
[string]$Name = 'John Doe'
}
Opis metody klasy przypomina opis funkcji, ale bez użycia słowa kluczowego function. Podobnie jak w funkcji, do metod w razie potrzeby przekazywane są parametry:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
#opis metody
Smile([bool]$param1)
{
If($param1) {
Write-Host ':)'
}
}
}
Teraz przedstawiciel naszej klasy potrafi się uśmiechać:
$object = [MyClass]::new()
$object.Smile($true)
Metody można przeciążać, ponadto klasa może mieć , a także konstruktorzy, których nazwy pokrywają się z nazwą samej klasy. Zdefiniowana w skrypcie lub module PowerShell klasa może być bazą dla innej – w ten sposób realizowane jest dziedziczenie. Jako klasy bazowe można używać już istniejących klas .NET:
class MyClass2 : MyClass
{
#ciało nowej klasy, której podstawą jest MyClass
}
[MyClass2]::new().Smile($true)
Nasze omówienie pracy z obiektami w PowerShell trudno określić jako wyczerpujące. W następnych publikacjach spróbujemy je pogłębić na praktycznych przykładach: piąty artykuł cyklu będzie poświęcony kwestiom integracji PowerShell z zewnętrznymi komponentami oprogramowania. Poprzednie części można znaleźć w linkach poniżej.
Źródło: habr.com
