PowerShell – efektywniej, część 7.
Minął prawie miesiąc od ostatniego wpisu… dla mnie był to miesiąc wyjątkowo długi. Kończymy powoli wdrażanie Windows 7 w naszej firmie, a wiadomo jak to jest – końcówki zwykle są najgorsze, szczególnie gdy innych spraw również nie można odłożyć na później. Skutek jest taki jak widać – blog musiał nieco poczekać… Dziś zamknąć zamierzam kwestie związane z zaawansowanymi funkcjami/ skryptami. Nasza zaawansowana (choć niezbyt funkcjonalna) ma już niemal wszystko. Brakuje tylko jednego – pomocy. W przypadku cmdletów pomoc jest tworzona przy pomocy plików maml. Pisanie ich na piechotę nie należy do wdzięcznych czynności, można to sobie znacznie uprościć korzystając z narzędzia Cmdlet Help Editor. Pisząc zaawansowane funkcje mamy zadanie znacznie uproszczone. W zasadzie całość sprowadza się do umieszczenia odpowiednio sformatowanego komentarza w odpowiednim miejscu. Zasady przypominają nieco regulamin dotyczący spalonego w piłce nożnej: niby wszystko jest proste, ale jak się zacznie wyjaśniać komuś, robi się coraz mniej “prosto”. W zasadzie budowanie pomocy sprowadza się do dodania komentarza w formacie:
<#
.Nagłówek
Treść
.Dwuczłonowy Nagłówek
Treść
#>
Dokładny opis wszystkich elementów znajduje się w pomocy (about_comment_based_help), ja wspomnę o najistotniejszych (moim zdaniem):
- Synopsis – krótki opis naszej funkcji
- Description – opis obszerniejszy, bardziej szczegółowy
- Example – przykłady, każdy przykład umieszczamy w osobnej sekcji Example a sekcji takich możemy definiować ile chcemy (nawet jeśli istnieje jakiś limit, to trudno będzie do niego “dobić”
- Notes – dodatkowe informacje, które nie pojawią się w widoku domyślnym
- Parameter MojParametr – opis konkretnego parametru
Ale to dopiero początek, dalej zaczyna się cała litania innych warunków, po kolei więc:
- można użyć zarówno komentarzy blokowych (<# #>), “tradycyjnych” (# jako pierwszy znak w linii) oraz mieszanki obu
- komentarz do funkcji można umieścić “w ciele”, lub tuż nad słowem kluczowym “function”
- umieszczając pomoc nad funkcją, możemy zostawić maksymalnie jedną linię pustą pomiędzy końcem komentarza a początkiem funkcji.
- umieszczając pomoc w ciele funkcji, musimy pamiętać by umieścić go przed blokiem param () i [CmdletBinding()]
- w przypadku pomocy do skryptu – musimy umieścić komentarz na jego początku lub końcu
- jeśli chcemy dodać linie #requires na początku skryptu – musimy odseparować ją pustą linią od bloku zawierającego pomoc
- umieszczenie pomocy na końcu skryptu i podpis elektroniczny wzajemnie się wykluczają (niedopatrzenie
) – podpis jest komentarzem, więc automatycznie blok pomocy nie jest ostatni i przestaje być obsługiwany
- jeśli chcemy pomoc skryptu umieścić na jego początku musimy zadbać o to, by pomoc nie “przykleiła” nam się do pierwszej funkcji znajdującej się w skrypcie
- jakakolwiek literówka w nagłówkach pomocy (n.p.: .Synosis) automatycznie sprawi, że pomoc się nie pojawi w ogóle
- każdy nagłówek musi być w osobnej linii, sekcja musi zaczynać się w linii następnej i kończyć w linii poprzedzającej następny nagłówek
- komentarz musi stanowić jedną całość – jeśli będą przerwy między częściami, brana pod uwagę będzie tylko ta część, której położenie zgadza się z zasadami wymienionymi powyżej
- opis parametrów można umieszczać zarówno tuż nad nimi, jak i w treści “głównej” pomocy
Jak widać – miejsc na potencjalne pomyłki nie brakuje. Próbowałem sobie wyłapywanie błędów (szczególnie literówek) ułatwić jakiś czas temu (dokładnie w czasie Scripting Games 2010) – wtedy też napisałem prostą funkcję, która miała mi w wyłapywanie błędów pomóc. Bazuje ona na wyrażeniach regularnych i pewnie wymagałaby poprawek – pisałem ją w czasach gdy o tych ostatnich wiedziałem zdecydowanie mniej niż teraz. Ale na pewno może się ona przydać od czasu do czasu, by wyłapać najbardziej bagatelne błędy…
Tyle zasady. A jak najlepiej budować taką pomoc? Synopsis i Description to moim zdaniem absolutne minimum. Dobrze jest też umieścić choć kilka przykładów użycia naszej funkcji. Odnośnie parametrów – preferuję umieszczania pomocy do nich bezpośrednio na nimi, w bloku param. Dzięki temu, moim zdaniem, łatwo tę pomoc “wyłapać” w czasie modyfikowania skryptu/ funkcji. A i dodając nowy parametr możemy od razu wzbogacić go o odpowiednią pomoc. Wyjątkiem będą tu parametry dodawane w zaawansowanych funkcjach automatycznie, jak WhatIf, Confirm, Debug i Verbose.
Pozostałe elementy pomocy używam niezmiernie rzadko. Możliwości jest sporo, więc najlepiej korzystać z nich z głową, by nie przesadzić… Jak pomoc będzie zbyt przeładowana – nikomu nie będzie się chciało jej czytać.
Na koniec nasza funkcja, tym razem już odpowiednio uzbrojona w system pomocy:
function Get-Foo { <# .Synopsis Wyświetla literki i cyferki. .Description Funkcja wyświetla literki i cyferki, w zależnosci od potrzeb. Liczby są formatowane jako waluta (C4), procenty (P4) i zwykłe liczby (N4). W przypadku słów nie jest używane żadne specjalne formatowanie. .Example Get-Foo Slowo Slowo to moje slowo Wyświetla wybrany string. .Example Get-Foo -Liczba .24 -Format P4 Moja liczba: 24.0000 % Wyświetla liczbę sformatowaną w wybrany sposób. .Parameter WhatIf Dodawany automatycznie. Pomoc do niego możemy umieścić jedynie w taki sposób. .Parameter Confirm I tu podobnie... #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Domyslny' )] param ( # Liczba, którą chcemy wyświetlić. [Parameter( Mandatory = $true, HelpMessage = 'Pomocy!', ValueFromPipeline = $true, Position = 0, ParameterSetName = 'Liczby' )] [double]$Liczba, # Pojedyncze słowo, które chcemy wyświetlić. [Parameter( Mandatory = $true, HelpMessage = 'Pomocy!', ValueFromPipelineByPropertyName = $true, Position = 0, ParameterSetName = 'Domyslny' )] [ValidatePattern('^\w+$')] [string]$Slowo, # Format, który zostanie użyty do wyświetlenia podanej liczby. [Parameter( Position = 1, ParameterSetName = 'Liczby', Mandatory = $true, HelpMessage = 'Pomozcie mi!' )] [ValidateSet('P4','N4','C4')] [string]$Format ) begin { Write-Verbose "Format: $Format" Write-Verbose "Liczba: $Liczba" Write-Verbose "Slowo: $Slowo" if ($Format) { Write-Debug "Wygląda na to, że używamy liczby." } } process { if ($psCmdlet.ShouldProcess( "Cel: $Slowo $Liczba", "Operacja: Wyświetl" )) { switch ($psCmdlet.ParameterSetName) { Domyslny { "{0} to moje slowo" -f $Slowo } Liczby { "Moja liczba: {0:$Format}" -f $Liczba } } } } }
W następnej części zamierzam zająć się modułami. Ponieważ temat ten jest dość obszerny, nie wykluczone że rozbiję go na kilka wpisów. Byleby tylko czasu i zapału na pisanie nie zabrakło…