Skripty a moduly
Další možností, jak uchovat naše PowerShellí výtvory pro budoucí generace jsou mimo funkcí (probraných v posledních dvou dílech) také skripty a moduly. Dnes si o nich něco povíme.


Skripty
Pokud máte vytvořeno více funkcí, můžete (a zřejmě i budete) je sdružovat do skriptů. Skript může být opět záležitostí na jednu řádku, ale pravděpodobněji budete vytvářet skripty o něco složitější. Skript je v podstatě kus kódu uložený do souboru s příponou PS1. Již v prvním díle tohoto seriálu jsme si řekli základní informace o spouštení funkcí, proto je již nebudeme opakovat.

C:\Scripts> 'Get-Process | Sort-Object Name | Select-Object -First 5' > skript1.ps1
C:\Scripts> .\script1.ps1
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
42 3 1224 3804 33 0,14 2240 AESTFltr
39 2 964 2480 20 0,08 4056 ApMsgFwd
41 2 948 2932 29 0,06 1680 ApntEx
88 3 2028 5780 37 1,02 576 Apoint
161 4 3480 5056 39 209,06 2156 BbDevMgr

Právě jsme vytvořili první skript, prostým zkopírováním příkazů do textového souboru. Tento soubor jsme spustili a dostali jsme naprosto stejné výsledky jako bez použití skriptu. Až potud žádné překvapení, pojďme dále.

C:\Scripts>$processCount = (Get-Process | Measure-Object).Count
C:\Scripts>$processCount
88

C:\Scripts> @'
>> $pc = (Get-Process | Measure-Object).Count
>> Write-Host $pc
>> '@ > script2.ps1

C:\Scripts>.\script2.ps1
88

C:\Scripts> $pc
C:\Scripts>

Vidíme, že proměnná pc neobsahuje žádnou hodnotu. Přesto skript proběhl, protože Write-Host nám počet procesů vypsal. Zde vstupuje do hry oblast platnosti proměnných (anglicky scope). Stručně řečeno, proměnné můžeme vytvářet tak, že budou „viditelné“ (uvidíme jejich hodnoty) pouze ve funkci, skriptu nebo globálně. Pokud neuvedeme výslovně oblast, platí všechny proměnné, vytvořené ve skriptu, pouze uvnitř tohoto skriptu a „zvenku“ (konzole) je nemůžeme přečíst. Což byl přesně předchozí případ. Proměnná pc vznikla uvnitř skriptu, cmdlet Write-Host ji vypsal na konzoli (uvnitř skriptu) a po jeho skončení zanikla i tato proměnná. Máme několik možností, jak tuto vlastnost obejít.

Uvedení platnosti proměnné v jejím názvu

Nejdříve opět předvedeme a poté si techniku okomentujeme.

C:\Scripts> @'
>> $Global:pc = (Get-Process | Measure-Object).Count
>> Write-Host $Global:pc
>> '@ > script3.ps1

C:\Scripts> .\script3.ps1
88

C:\Scripts> $pc
88

Skript je stejný, ale před vlastním jménem proměnné jsme uvedli modifikátor Global. Tím určujeme, že proměnná bude platit globálně a tedy i v konzoli.

Dot-sourcing

Druhou možností je takzvaný dot-sourcing (nebudu se pouštět do čekého překladu 🙂 Pomocí tečky (.) určíme, že skript má „proběhnout globálně“ Tím zajistíme, že všechny proměnné zůstanou viditelné globálně i po skončení vlastního skriptu.

Nejprve smažeme proměnnou pc, která už globálně existuje.

C:\Scripts> Remove-Variable pc
C:\Scripts> $pc
C:\Scripts>

A nyní spustíme znovu druhý skript, který nám dříve „nefungoval“.

C:\Scripts> . .\script2.ps1
89

C:\Scripts> $pc
89

Všimněte si tečky uvedené před voláním skriptu. Ta nám zajistí dot-sourcing.

Pokud se ptáte, jestli je možné u funkcí použít parametry, odpověď je ano. Pro parametry a nápovědu platí stejná pravidla jako pro funkce.


Profil
Pokud budete pracovat v PowerShellu delší dobu, začnete si možná upravovat prostředí tak, aby vám více vyhovovalo. Ať již se jedná a vytváření vlastních aliasů, definici proměnných nebo nebo například o vlastní skripty, může se hodit, mít je v konzoli okamžitě připravené k použití. Uveďme si příklad. Často pracujete s měnami a hodilo by se vám, kdybyste měli funkci na převod mezi dolary a korunami jednoduše dostupnou.

C:\Scripts> @'
>> function Get-Dolar
>> {
>> param([int]$castka)
>> Write-Output $($castka/18)
>> }
>> '@ > get-dolar.ps1

Skript spustíme pomocí dot-sourcingu (jinak by se nám funkce „neobjevila“ s globální platností a můžeme převádět koruny na dolary (prosím teď neřešme funkci jako takovou – jistě je na ní mnoho co vylepšovat).

C:\Scripts > . .\get-dolar.ps1
C:\Scripts > Get-Dolar 72
4

Vidíme, že funkce funguje a můžeme ji používat. Při dalším spuštění PowerShellu budeme ovšem muset opět zavolat skript, abychom mohli funkci použít. V tomto případě přichází na řadu profil. Stručně řečeno je profil skript, který se spouští při startu PowerShellu. Na vašem počítači je uložen v:

C:\Scripts> $PROFILE.AllUsersAllHosts
C:\WINDOWS\system32\WindowsPowerShell\v1.0\profile.ps1

Standardně není vytvořený a pokud jej nemáte stačí jej vyrobit například v notepadu. V okamžiku, kdy máte profil vytvořený, můžete do něj dávat např. funkce, které často využíváte. Pojďme si tedy takový profil vytvořit.

C:\Scripts> gc $PROFILE.AllUsersAllHosts
Get-Content : Cannot find path 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\profile.ps1' because it does not exist.
At line:1 char:3
+ gc <<<< $PROFILE.AllUsersAllHosts
+ CategoryInfo : ObjectNotFound: (C:\WINDOWS\syst...1.0\profile.ps1:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

C:\Scripts> @'
>> function Get-Dolar
>> {
>> param([int]$castka)
>> Write-Output $($castka/18)
>> }
>> '@ > $PROFILE.AllUsersAllHosts
>>

C:\Scripts> gc $PROFILE.AllUsersAllHosts
function Get-Dolar
{
param([int]$castka)
Write-Output $($castka/18)
}

Potřebnou funci máme uloženou. Nyní je potřeba znovu spustit PowerShell. Po spuštění můžeme vyzkoušet naši známou konverzi.

C:\Scripts> Get-Dolar 72
4

Více o profilech se můžete dočíst v tématické nápovědě about_Profiles. Důležité je například to, že profil není pouze jeden nebo že profily jsou uloženy v různých cestách.


Moduly
Po funkcích a skriptem postoupíme do dalšího levelu a podíváme se na moduly. Zde si dovolím malou odbočku. Několik let zpátky jsem se staral o systém jehož výstupem bylo velké množství různých log souborů. Vzhledem k tomu, že LogParser byl v té době hudbou budoucnosti, hledal jsem možnosti, jak tyto logy prohledávat. Nakonec jsem skončil u jazyka Perl. Vzhledem k tomu, že se mé skripty rozrůstaly a komplexnost se zvětšovala, potřeboval jsem stále dokonalejší techniky. A tehdy příšel na řadu CPAN – zdroj stovek skriptů od různých autorů pokrývajících snad všechny myslitelné oblasti. Stačilo jen najít správný skript (modul) a „doinstalovat“ jej do mého prostředí. CPAN dává jako příklad i Bruce Payette ve své knize PowerShell in Action.

Idea modulů je tedy jednoduchá – umožnit různým autorů vytvářet „skripty“ které budou snadno přenositelné mezi počítači a pro složitější úkony budou poskytovat větší funkcionalitu než skripty. V PowerShellu v1 bylo jediným možným rošířením použití tzv. snap-inů. Například vynikající cmdlety pro správu Active Directory od firmy Quest byly poskytovány ve formě snap-inů (tedy v podobě DLL souborů). V současné době je velké množství rozšíření PowerShellu poskytováno ve formě modulů (tedy text). Moduly ovšem mohou být i binární, více viz dále.

Nejdříve si ukážeme práci s již existujícími moduly a poté si ukážeme, jak vytvořit moduly vlastní.

Kontrolní otázka: Zkuste si tipnout – jaký cmdlet použít pro zjištění dostupných modulů? Pokud jste odpověděli Get-Module, máte samozřejmě pravdu. Pojďme si ukázat příkazy, které použije při práci s moduly. Opět nejdřív ukážeme celý kód a poté jej okomentujeme.

C:\Scripts> Get-Module
C:\Scripts> Get-Module -ListAvailable

ModuleType Name ExportedCommands
---------- ---- ----------------
Script Cookbook {}
Script CP2010 {}
Script DotNet {}
Manifest FileSystem {}
Script ISEDemo {}
Manifest IsePack {}
Manifest PowerShellPack {}
Manifest PSCodeGen {}
Manifest Pscx {}
Manifest PSImageTools {}
Manifest PSRemoteRegistry {}
Manifest PSRSS {}
Manifest PSSystemTools {}
Manifest PSUserTools {}
Manifest SMS2003 {}
Manifest TaskScheduler {}
Manifest WPK {}
Manifest BitsTransfer {}

C:\Scripts> Import-Module PSRemoteRegistry
C:\Scripts> gmo

ModuleType Name ExportedCommands
---------- ---- ----------------
Script PSRemoteRegistry {Get-RegKey, Get-RegBinary, Get-RegQWord, Get-RegMultiString...}

C:\Scripts> ipmo BitsTransfer
C:\Scripts> ipmo wpk
C:\Scripts> gmo

ModuleType Name ExportedCommands
---------- ---- ----------------
Script PSRemoteRegistry {Get-RegKey, Get-RegBinary, Get-RegQWord, Get-RegMultiString...}
Manifest bitstransfer {Start-BitsTransfer, Remove-BitsTransfer, Resume-BitsTransfer, Get-BitsT...
Script wpk {Get-DependencyProperty, New-ModelVisual3D, New-DiscreteVector3DKeyFrame...
C:\Scripts> Remove-Module ps*
C:\Scripts> rmo b*
C:\Scripts> New-Label "$((gmo).name)" -show

clip_image001

C:\Scripts> gmo|rmo
C:\Scripts> gmo

Jak jsme již řekli, pro zobrazení instalovaných modulů slouží cmdlet Get-Module zadaný bez parametrů. Jelikož nebyl žádný modul použit, nezobrazilo se ve výpisu nic. Proto jsme použili parametr ListAvailable. Při jeho použití se PowerShell podívá do všech adresářů s moduly (adresáře jsou uvedeny v proměnné prostředí $env:PSModulePath) a vypíše moduly, které můžeme importovat.

Abychom moduly mohli použít, musíme je importovat pomocí Import-Module. Poté je můžeme použít, respektive můžeme volat funkce (nebo cmdlety) v nich obsažené. Zde ukázáno na příkladu modulu WPK (součást Windows PowerShell Pack – více viz 9. část tohoto seriálu). Na konci práce můžeme modul odebrat pomocí Remove-Module. Další příkazy pro práci s moduly (včetně aliasů) můžete zobrazit například takto:

C:\Scripts> gcm *module* -C cmdlet | sort name |% {"{0} {1}" -f $_.Name, $(gal -def $_.Name -ea 0)}
Get-Module gmo
Import-Module ipmo
New-Module nmo
New-ModuleManifest
Remove-Module rmo
Test-ModuleManifest

A jako vždy – podrobnější informace o práci s moduly najdete v tématické nápovědě about_modules.