PowerShell TabExpansion Plus Plus
Wraz z pojawieniem się PowerShella w wersji 3 zyskaliśmy nieco szersze możliwości dopełniania tabulatorem różnych dynamicznych elementów: nazw procesów, modułów, typów. Wszystko to wygląda smakowicie, ale od razu rodzi się pytanie: czemu nie mogę tej samej logiki zastosować dla innych dynamicznych wartości? Nazw wirtualnych maszyn, PowerShellowych wtyczek, itd? Temu właśnie służy moduł TabExpansion++ – umożliwia on pisanie własnych instrukcji, w oparciu o które tabulator (czy też związanym z nim IntelliSense) będzie w stanie “odgadnąć” wartości dopełnianych argumentów.
Słów kilka o autorze
Jason Shirk, który jest autorem tego modułu (oraz innego ciekawego dodatku, PSReadLine, o którym wkrótce zamierzam napisać nieco szerzej) jest jednym z ludzi zaangażowanych w tworzenie PowerShella. Przez jakiś czas przeniósł się do innego zespołu, i chyba bardzo tęsknił za pracą nad PowerShellem, gdyż właśnie w tym czasie napisał oba wspomniane moduły. Jeśli chcecie posłuchać samego Jasona, to w sieci można znaleźć kilka nagrań z jego udziałem:
- wywiad z PowerShell Deep Dive we Frankfurcie
- PowerScripting Podcast z jego udziałem
- Hanselminutes Podcast z udziałem Jasona i Lee Holmesa
Jason zajmował się w znacznym stopniu wszystkimi cudeńkami, które mnie osobiście w wersji trzeciej bardzo radowały. TabExpansion++ to w zasadzie kolejny krok w tym kierunku. Gdy tylko usłyszałem o module i możliwości dołożenia swoich kilku cegiełek, od razu zabrałem się do pracy.
Instalacja
Zanim zaczniemy z modułu korzystać, musimy go najpierw zainstalować. Możliwości są w zasadzie dwie. Przede wszystkim możemy sami ściągnąć moduł z GitHuba. Instalacja nie różni się od innych modułów: trzeba najpierw “odbezpieczyć” plik, rozpakować w folderze znajdującym się w Waszym PSModulePath i wreszcie zmienić nazwę folderu tak, by pokrywała się z nazwą samego modułu (TabExpansion++).
Druga możliwość to użycie skryptu:
(new-object System.Net.WebClient).DownloadString( 'https://raw.github.com/lzybkr/TabExpansionPlusPlus/master/Install.ps1' )
Można oczywiście najpierw ściągnąć skrypt i uruchomić go świadomie. Osobiście wybieram samodzielną instalację.
Co dalej?
W zasadzie od razu można moduł dodać i zacząć korzystać z tego, co już zostało napisane, czy to przez Jasona, czy też przez innych ludzi uczestniczących aktywnie w tym projekcie. Obecnie lista obejmuje około 500 różnych “dopełnień” dla różnych poleceń i ich parameterów. Nic nie musimy specjalnie robić, by uzyskać całkiem ciekawe efekty. Na początku wspomniałem wirtualne maszyny:
Oczywiście, wygodnie też jest móc dopełniać wtyczki:
Czy też nazwy obiektów COM:
Ale to nie wszystko: dzięki dość ciekawym zabiegom, możemy też dopełniać polecenia “natywne”: netsh, net, a ostatnio – również robocopy:
A co z poleceniem <TU WSTAW SWOJE IMIĘ>?
To oczywiście nie wszystko. Podobnie jak dopełnienia wielu poleceń zostały napisane przez osoby zaangażowane w ten projekt, tak samo użytkownik końcowy może dodawać “uzupełnienia” dla innych poleceń, z których korzysta: czy to własnych, czy też pochodzących z innych źródeł. By to ułatwić, moduł zawiera przydatne w ISE w wersji trzeciej skrawki kodu, dzięki którym możemy od razu przystąpić do dzieła. Wszystkie skrawki związane z TabExpansion++ mają odpowiedni prefiks, dzięki czemu bez trudu je odnajdziemy. Zanim zaczniemy z nich korzystać, musimy je zaimportować:
Import-IseSnippet -Module TabExpansion++
W większości przypadków będzie interesować nas “ArgumentCompleter – PowerShell commands”. Wystarczy nazać odpowiednio funkcję (nie będziemy jej używać bezpośrednio, więc nazwa może być dowolna), wybrać interesujący nas parametr, określić polecenia, których dopełnienie ma dotyczyć i wreszcie, podać odpowiedni opis. Dalej musimy już tylko zadbać o to, by nasza funkcja podpowiadała odpowiednie wartości dla interesującego nas argumentu. Dla przykładu, weźmy polecenie Get-Constructor z modułu Reflection. Polecenie to ułatwia znacznie pracę z obiektami .NET, prezentując nam możliwości ich inicjalizacji. Co zrobić więc by uzyskać efekt:
No cóż, nie pozostaje nam nic innego, jak tylko zdefiniować odpowiednią funkcję, która zrobi to dla nas. Skorzystamy przy tym ze statycznej metody klasy [Management.Automation.CompletionCompleters]
function Reflection_TypeParameterArgumentCompletion { [ArgumentCompleter( Parameter = 'Type', Command = { Get-CommandWithParameter -Module Reflection -ParameterName Type }, Description = 'Complete type names' )] [ArgumentCompleter( Parameter = 'TypeName', Command = { Get-CommandWithParameter -Module Reflection -ParameterName TypeName }, Description = 'Complete type names' )] param( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter ) [Management.Automation.CompletionCompleters]::CompleteType( $wordToComplete ) }
Jak widać, zamiast ręcznie podawać listę interesujących mnie poleceń, możemy wygenerować ją przy pomocy specjalnie w tym celu zdefiniowanej funkcji Get-CommandWithParameter. Funkcję taką musimy zapisać w pliku .ps1 lub .psm1 w jednej z lokalizacji wymienionych w $env:PSModulePath i (domyślnie nieistniejącej) $env:PSArgumentCompleterPath.
Źródłem listy pasujących wartości może być dosłownie wszystko, ważne by funkcja zwracała obiekty typu System.Management.Automation.CompletionResult. Na ogół będziemy w celu ich utworzenia korzystać z kolejnej funkcji, New-CompletionResult. Prosty przykład: Get-View (VMware) dopuszcza tylko kilka wartości dla parameteru ViewType. “Normalny” tab na nie niestety nie działa, spróbujmy to naprawić. Tym razem skorzystamy z drugiej metody, polecenia Register-ArgumentCompleter:
Register-ArgumentCompleter -CommandName Get-View -ScriptBlock { param( $commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter ) $Lista = 'ClusterComputeResource','ComputeResource', 'Datacenter', 'Datastore', 'DistributedVirtualPortgroup', 'DistributedVirtualSwitch', 'Folder', 'HostSystem', 'Network', 'ResourcePool', 'StoragePod', 'VirtualApp', 'VirtualMachine', 'VmwareDistributedVirtualSwitch' -like "*$wordToComplete*" foreach ($Typ in $Lista) { New-CompletionResult -CompletionText $Typ -ToolTip $Typ } } -ParameterName ViewType -Description 'Completes Get-View –ViewType'
Efekt jest natychmiastowy:
Bez tego modułu podobny efekt można uzyskać tworząc funkcję zastępczą, ale to na ogół gra nie warta świeczki. Tu niewielkim wysiłkiem uzyskujemy ten sam efekt, bez modyfikowania samego polecenia.
Coś jeszcze?
Na koniec jedna rzecz: siłą tego modułu są dopełniane polecenia. Im więcej poleceń będzie wspieranych, tym większa będzie korzyść z korzystania z niego. By sytuację poprawić można zrobić kilka rzeczy:
- włączyć się do projektu (ważne: Jason chce by rdzeń zawierał jedynie dodatki dla poleceń rdzennych, czyli raczej nie ma sensu “wrzucać” dodatków dla produktów spoza standardowego systemu Windows)
- dodawać rozszerzenia dla własnych modułów: zwykły plik .ps1, który nie jest importowany, więc widoczny jedynie dla TabExpansion++
- udostępniać dodatki również dla innych poleceń/ modułów (gists?)
Oczywiście można też używać i zgłaszać wszelkie usterki, albo po prostu – używać i zachęcać do używania innych. Dla mnie (mistrza literówek) ten moduł to absolutnie niezbędne narzędzie…
[…] komendy znak po znaku. Szczęśliwie, podobnie jak to miało miejsce w przypadku rozbudowywania tabulatora, aby móc tworzyć swoje własne “regułki”, nie musimy budować wszystkiego od zera. […]
(new-object System.Net.WebClient).DownloadString( ‚https://raw.github.com/lzybkr/TabExpansionPlusPlus/master/Install.ps1’ ) | iex
chyba?