Profile – część czwarta.
Jako miłośnik rozwiązań “wszystko w jednym” jestem gorącym zwolennikiem pracy w PowerShell ISE. ‘Normalną’ konsolę odpalam sporadycznie, na ogół po to, by przetestować rozwiązania pisane w ISE. Host ten do niedawna dawał wyjątkową możliwość rozbudowywania narzędzia ‘od środka’. Przy pomocy zmiennej $psISE można dodawać menu, otwierać i zmieniać zawartość plików, zmieniać opcje kolorystyczne, rozmieszczenie paneli, wielkość czcionki itd, itp. Obecnie kolejne hosty zaczynają oferować podobne rozwiązania (prym wiedzie w tym darmowe PowerGUI), ja jednak pozostałem wierny PowerShell ISE. Może to sentyment, może przyzwyczajenie (które jak wiemy jest drugą naturą człowieka). W związku z możliwością rozbudowy i częstotliwością używania mój profil w ISE rósł w oczach. Wkrótce okazało się, że część funkcjonalności zaczęła żyć własnym życiem – wtedy to zdecydowałem się tę część upublicznić i zamknąć w module. ISEFun miało być po prostu zestawem funkcji często przeze mnie w ISE wykorzystywanych (jedną z nich już prezentowałem – Edit-Function), ale ostatecznie zaczęło się to rozrastać na cały zestaw narzędzi, przy pomocy których upraszczałem sobie pracę w ISE. Ten host ma jednak dla mnie przede wszystkim inną zaletę. Mogę w nim utworzyć kilka niezależnych ‘konsol’ – każda ze swoim zestawem zmiennych, załadowanych modułów, historię poleceń, kolekcję otwartych plików… Łączy je $psISE. I tu zaczyna się zabawa.
Szybki start i bogactwo narzędzi.
PowerShell, gdy go ciągle rozbudowujemy o kolejne dodatki, staje się coraz wolniejszy. Uruchamia się długo, a czasem chcemy wykonać jakieś proste polecenie szybko, nie tracąc czasu na ładowanie wszystkich dodatków. I rodzi się dylemat: jak tu zjeść ciastko i mieć ciastko? ISE daje możliwość problem ten obejść. Wystarczy prosty manewr: dwa ‘okienka’ na starcie. Jedno ‘gołe’, drugie w tle ładuje wszystko co nam do szczęścia potrzebne. Po uruchomieniu moje ISE wygląda tak:
Zakładka ‘NoProfile’ jest golutka i startuje szybciutko, zaraz też uruchamia drugą zakładkę, PoSh # 1, gdzie zaczynają się ładować dodatki (między innymi moje ISEFun). W tym czasie ISE wraca grzecznie na pierwszą zakładkę. Mogę działać. 🙂 A gdy druga zakładka zrobi swoje – mogę robić wszystko to, czego bez wtyczek robić się nie da. Dodatkowo tworzę sobie menu, gdzie figurują wszystkie moje zakładki (mogę sie między nimi przełączać skrótek klawiszem ALT + nr zakładki). Reszta mojego profilu to już w zasadzie kosmetyka (lubię czerń, jak widać na załączonym obrazku 😉 ) i ładowanie modułów. W tym mojego własnego, ISEFun, któremu poświęcić zamierzam osobny artykuł. Mój profil, który robi dla mnie te kilka milutkich usprawnień wygląda tak:
if ($curtab -eq $tabs[0]) { $curtab.DisplayName = 'NoProfile' $newTab = $tabs.Add() } else { function prompt { $curtab.DisplayName = "PoSh # $($tabs.IndexOf($curtab))" $(if (test-path variable:/PSDebugContext) { '[DBG]: ' } else { '' }) + 'PS ' + $(Get-Location) + $(if ($nestedpromptlevel -ge 1) { '>>' }) + '> ' } $curtab.DisplayName = "PoSh # $($tabs.IndexOf($curtab))" $NewTabIndex = $tabs.Count $SwitchToMe = [ScriptBlock]::Create( "`$tabs.SetSelectedPowerShellTab(`$tabs[$($NewTabIndex-1)])" ) for ($i = 0; $i -lt $tabs.Count - 1; $i++) { if ($NewTabIndex -le 9) { $SwitchTo = [ScriptBlock]::Create( "`$tabs.SetSelectedPowerShellTab(`$tabs[$i])") $MyMenu = $tabs[$i].AddOnsMenu.Submenus | Where-Object { $_.DisplayName -eq 'Tabs' } try { [void] $Mymenu.Submenus.Add($curtab.DisplayName, $SwitchToMe , "ALT + $NewTabIndex") } catch { Write-Debug 'This menu item is already present!' } } else { # That should not happen cause limit is 8 Write-Host -ForegroundColor Red "Can not add this tab" } [void] $TabsMenu.Submenus.Add($tabs[$i].DisplayName, $Switchto, "ALT + $($i+1)") } $tabs.SetSelectedPowerShellTab($tabs[0]) $tabs[0].CommandPane.Focus() Add-PsSnapin Quest* Import-Module ('WPK IsePack Inv ISEFun Reflection PSCX'.Split()) . "C:\Program Files\Quest Software\Management Shell for AD\qsft.ps1" }
Jak widać nie jest przesadnie rozbudowany. Całe bogactwo rozszerzeń to trzy ostatnie linie. Byłaby jedna, gdybym wreszcie znalazł chwilkę i korzystająć z modułu Reflection ‘przepisał’ narzędzia Questu do modułu. Reszta odpowiada za stworzenie na początku dwóch ‘zakładek’, a przy tworzeniu kolejnych – za modyfikację menu ‘Tabs’ –> dodaje nowoutworzoną zakładkę do pozostałych, i na odwrót – pozostałe w nowoutworzonej. Całość poprzedza jeszcze kilka linii tworzących ‘skróty’ do elementów $psISE i wyżej wspomniana kosmetyka:
$Host.UI.RawUI.WindowTitle = "Only {0} days to Scripting Games!" -f (New-TimeSpan -End '4/4/11').Days $tabs = $psISE.PowerShellTabs $curtab = $tabs[$tabs.Count-1] $ISEOpt = $psISE.Options $ISEOpt.OutputPaneBackgroundColor = 'black' $ISEOpt.OutputPaneTextBackgroundColor = 'black' $ISEOpt.OutputPaneForegroundColor = 'white'
No i naturalnie linia przypominająca mi o tym, że Scripting Games 2011 zbliżają się nieubłaganie… 🙂 Ale to element, który już wkrótce zniknie. Jeszcze tylko 4 dni. 😉
[…] Część 4: PowerShell ISE […]
Bartku – fajnie piszesz, ale dla początkujących, którzy z PS zaczynają swoją przygodę (jak ja) brakuje kilku prostych informacji, np: gdzie wkleić udostępniony kod, by ISE uruchamiał się z dodatkową zakładką ….
czy do $profile … czy CurrentUserCurrentHost …
mi nie udało się na razie uruchomić nowej zakładki
Cannot index into a null array.
At line:1 char:23
+ if ($curtab -eq $tabs[ <<<< 0]) {
+ CategoryInfo : InvalidOperation: (0:Int32) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
Faktycznie, coś „zeżarło” początek profilu, w którym zmienna $tabs jest definiowana. Postaram się to w miarę szybko poprawić. Odnośnie tego „gdzie” – $profile i $profile.CurrentUserCurrentHost mają dokładnie tę samą wartość. 🙂 „Goły” $profile to string, który wskazuje właśnie profil dla bieżącego użytkownika i bieżącej powłoki.
EDIT jednak nie zeżarło, choć faktycznie – logiczniej byłoby fragmenty kodu ustawić w porządku zgodnym z ich kolejnością w pliku. W tej chwili niżej możesz przeczytać: Całość poprzedza jeszcze kilka linii tworzących ‘skróty’ do elementów $psISE i wyżej wspomniana kosmetyka:. Ten fragment kodu musi znaleźć się na początku, by całość powyżej zadziałała. 6 dla mnie za umiejętność zagmatwania czegoś stosunkowo prostego… 😉 Sorry.
Szacun … teraz działa, mogę ruszyć z dalszym czytaniem