Nadchodzi PowerShell z klasą!

muszka3PowerShell jest mocno osadzony w .NET. Wydaje się więc naturalne, że powinno się dać w nim tworzyć klasy. I to nie “na okrętkę”, kompilując w locie kod z języków tradycyjnie do tego przeznaczonych (C#, VB.NET), przy pomocy poleceń takich jak Add-Type. Wydawało się naturalne, że w PowerShellu powinno być możliwe tworzenie klasy w sposób zbliżony do tego, w jaki tworzymy funkcje, skrypty, workflowy (od wersji 3), konfiguracje (od wersji 4). Innymi słowy: korzystając ze składni, którą znamy dość dobrze.

Dawno, dawno temu zapragnąłem stworzyć sobie klasę w PowerShellu. Jakież było moje zdziwienie, gdy próba zdefiniowania ‘pustej’ klasy nie zakończyła się całkowitym fiaskiem i dała mi nadzieję, że może kiedyś:

PowerShell-Class-Keyword-Not-Supported-v2

Minęło kilka lat, PowerShell ewoluował, a klasy pozostawały jedynie pieśnią przyszłości. Aż wreszcie przyszedł lipiec roku 2014 i oto są: klasy zawitały do PowerShella! Uśmiech

Eksperymentalny, czyli niedoskonały

Zanim jednak przejdziemy do omówienia tej nowości (jednej z kilku, które ogłoszono już w związku z kolejną, piątą już odsłoną PowerShella) wypada wspomnieć o eksperymentalnym charakterze wersji, którą trzeba zainstalować by funkcję tą przetestować. Zmieniło się bowiem podejście do rozwoju PowerShella. Do tej pory nowości pojawiały się wraz z nową wersją systemu, by po kilku miesiącach (w postaci Windows Management Framework, w skrócie WMF) pojawić się jako opcjonalna aktualizacja dla starszych systemów. Wersja piąta odwraca nieco kolejność. WMF dostępny jest jedynie dla wersji ostatnio wydanej (Windows Server 2012 R2/ Windows 8.1), jeszcze przed opublikowaniem kolejnej wersji systemu. Gdy funkcje w PowerShellu ulegną “zamrożeniu”, rozpocznie się praca nad przygotowaniem pakietów dla systemów starszych. Dzięki temu można wszelkich nowości “posmakować” przed oficjalną premierą następnej wersji Windowsa.

Dodatkowo wyróżnione są wersje mniej lub bardziej stabilne. Warto wspomnieć, że wersja z której korzystałem przy pisaniu tego artykułu (lipiec 2014) zalicza się do tych mocno eksperymentalnych. Czytaj: nie zaleca się instalowania poza labami. Puszczam oczko

Pobrać, zainstalować, testować…

Wersję WMF w której dodano klasy można pobrać tutaj (x64). Instalacja raczej nie wymaga instrukcji: zgadzamy się na wszystko, uruchamiamy system ponownie kiedy nas o to proszą. W razie problemów sprawdzamy co ma do zaoferowania dokument “Release notes”. Osobiście zainstalowałem pakiet na maszynie wirtualnej – wolę nie sprawdzać, co przestanie działać, jeśli zainstaluję pakiet ten gdziekolwiek indziej, włącznie z moim “poligonowym” laptopem. Jeśli jesteście ciekawi co jeszcze można znaleźć w tej wersji, to polecam lekturę wspomnianego wcześniej dokumentu: pomijając klasy (o których dokument ten jedynie wspomina) nowości pojawiające się wraz z wersją 5 są w nim dość dobrze opisane.

Startujemy!

Definiowanie klasy w PowerShellu wymaga zadbania o pewne elementy, które w “normalnym” użyciu niezbędne nie są. Do definiowania metod używamy słowa kluczowego ‘def’. Metody, podobnie jak w językach kompilowanych, muszą zwracać określone typy (jeśli nie podamy żadnego typu to z założenia metoda nie powinna nic zwracać). Oczywiście, metody mogą przyjmować parametry. Tworząc metody o nazwie takiej jak klasa zwracające typ ‘void’ definiujemy konstruktory naszej klasy. Warto przy tym zadbać o konstruktor bezparametrowy (będzie niezbędny, jeśli zamierzamy używać dostępnej od wersji trzeciej składni [Typ]@{ Wlasciwosc = ‘Wartosc’ }.

Oprócz tworzenia klas możemy też tworzyć typy wyliczeniowe. W tym wypadku skorzystamy ze słowa kluczowego ‘enum’, następnie podając nazwę typu i w bloku skryptu – poszczególne wartości tekstowe wraz z odpowiadającymi im wartościami liczbowymi:

enum Rola {            
    Pracownik = 20            
    Kontraktor            
}

Niestety, w chwili obecnej nie da się typów “zagnieżdżać”. Nie możemy więc typu zdefiniowanego w ten sposób użyć w bardziej rozbudowanej klasie. Definiując klasę przestrzegać musimy kilku zasad:

  • wszelkie zmienne stają się właściwościami
  • każda właściwość ma zdefiniowany typ
  • metody definiujemy korzystając ze wspomnianego wcześniej słowa kluczowego ‘def’
  • możemy ograniczyć zakres właściwości (n.p. uczynić ją prywatną) korzystając z przedrostka ‘private:’
  • tworząc konstruktory nie możemy używać nazw właściwości dla parametrów
  • jeśli utworzymy choć jeden własny konstruktor, to przestaje działać konstruktor bezparametrowy (chyba, że taki konstruktor również uwzględnimy)

Przykładowa klasa:

class Pracownik {            
    $Rola            
    [string]$SamAccountName            
    [string]$GivenName            
    [string]$Surname            
    [string]$Department            
    [string]$Title            
    [string]$Description            
    [int]$Id            
    [DateTime]$Zatrudniony            
    [DateTime]$Zwolniony            
    [string] def ToString {            
        $out = 'Employee {0}, {1} - {2}' -f @(            
            $this.Surname,             
            $this.GivenName,             
            $this.Title            
        )            
        return $out            
    }            
    [string]$Private:Test            
            
    def Pracownik {}            
            
    def Pracownik {            
        param (            
            [string]$ImieNazwisko            
        )            
        $this.GivenName, $this.Surname = $ImieNazwisko.Split()            
    }            
            
    def Employee {            
        param (            
            [string]$Imie,            
            [string]$Nazwisko            
        )            
        $this.GivenName = $Imie            
        $this.Surname = $Nazwisko            
    }            
            
    def Employee {            
        param (            
            [string]$Imie,            
            [string]$Nazwisko,            
            [string]$Tytul            
        )            
        $this.GivenName = $Imie            
        $this.Surname = $Nazwisko            
        $this.Title = $Tytul            
    }            
}

Tak utworzony typ jest “widziany” przez wszelkie narzędzia, które z typów korzystają (w tym Intellisense). Jeśli więc zdefiniujemy naszą klasę, możemy w prosty sposób stworzyć funkcję, która typ ten zwróci a wszelkie właściwości będą już znane:

function New-Employee {            
[OutputType([Pracownik])]            
param (            
    [string]$GivenName,            
    [string]$SurName,            
    [string]$Title,            
    [DateTime]$Zatrudniony,            
    [DateTime]$Zwolniony,            
    [Int]$Id,            
    [string]$Department,            
    [Rola]$Rola            
)            
    $PSBoundParameters.Add(            
        'SamAccountName',            
        ('{0}{1}' -f $GivenName, $SurName[0])             
            
    )            
    [Pracownik]$PSBoundParameters            
}

”OutputType” działa zgodnie z przewidywaniami. Pisząc skrypt, nawet bez jego uprzedniego uruchamiania, uzyskamy podpowiedzi odnośnie dostępnych właściwości:

PowerShell-Class-Intellisense

Łyżka dziegciu

Wszystko to wygląda obiecująco, ale widać wyraźnie, że rozwiązanie to nie jest jeszcze kompletne. Kilka prostych przykładów:

  • brak przestrzeni nazw
  • ignorowanie metody ToString() przy konstrukcjach takich jak “$NaszTyp”
'Nazwa typu'            
"$foo"            
'Nasza implementacja'            
$foo.ToString()

  • dopełnianie tabulatorem czasem “szaleje” i dopełnia nie nazwę częściową a pełną:

PowerShell-Class-Pelna-Nazwa-Przy-New-Object

Jest też pewnie wiele innych, drobnych usterek. Fakt pozostaje faktem: klasy zawitały do PowerShella, możemy więc (po raz kolejny) ograniczyć konieczność korzystania “objeść” problemu, który utrudnia nieco tworzenie bardziej rozbudowanych skryptów: możliwość prostego tworzenia własnych typów bez znajomości składni C# czy VB.NET.

~ - autor: Bartek Bielawski w dniu 30 lipca, 2014.

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj /  Zmień )

Zdjęcie na Facebooku

Komentujesz korzystając z konta Facebook. Wyloguj /  Zmień )

Połączenie z %s

 
%d blogerów lubi to: