Seriál Windows Powershell: Moduly (část 15.)

Při našem posledním setkání jsme si ukázali úvod do modulů. Naučili jsme se, jak je importovat do PowerShellu a jak zjistit, které moduly máme již importované. Zajisté jste dali na mou radu a přečetli si tématickou nápovědu about_modules J Dnes si ukážeme, jak vytvořit modul vlastní.

Nejdříve si „hodíme“ potřebné příkazy do konzole:
PS C:\> $wc = New-Object Net.WebClient
PS C:\> $link = 'https://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt'
PS C:\> $wc.DownloadFile($link, 'C:\Scripts\kurzy\kurzy.txt')
PS C:\> gc 'C:\Scripts\kurzy\kurzy.txt' |? { $_ -match 'EUR' } |% { $_ -match '\|(?<kurz>\d{2}.\d{1,3})$'|Out-Null; [double]$matches.kurz.replace(',', '.')

25,01

Na první řádce vytvoříme objekt WebClient. Pomocí tohoto objektu můžeme stahovat data z dané webové stránky (velmi, velmi zjednodušeně řečeno – tato třída toho umí daleko více, ale pro nás je momentálně důležité, že máme proměnnou, pomocí které můžeme stáhnout text dané stránky). Do proměnné $link si uložíme cestu, ze které budeme stahovat daný soubor (podle cesty je zřejmé, že nám půjde o kurzy měn na stránkách ČNB). Na třetím řádku použijeme metodu DownloadFile a uložíme daný soubor na lokální disk. Na posledním řádku už pouze filtrujeme ze souboru potřebné údaje – pro filtrování jsme použili regulární výraz (o regulárních výrazech si povíme v některém z dalších pokračování). Na posledním řádku už je vidět kurz pro aktuální den.

Je vidět, že jsme obdrželi požadovaná data, ale k dokonalosti nám samozřejmě hodně chybí. Pokud budeme chtít spustit stejné příkazy zítra, nebude příliš efektivní přepisovat celý kód znovu. Proto by se nám hodilo uložit všechny potřebné příkazy do jednoho souboru. Mohli bychom použít skript, ale pro studijní účely si vytvoříme modul. Tento modul bude obsahovat funkci pro stažení kurzů z webu, funkce pro přepočet z EUR a USD, potřebné aliasy. Modul nazveme Kurzy.

Nejprve si zobrazíme aktuální moduly začínající na ‘k’.

PS C:\> Get-Module k* -list

Žádný takový modul neexistuje. V minulém díle jsme uváděli, že moduly jsou uloženy v adresářích uložených v proměnné prostředí PSModulePath.

PS C:\Scripts\kurzy> $env:PSModulePath -split ';'
C:\Documents and Settings\moravec\My Documents\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\
Dropbox:\PowerShell\Profile

První dvě cesty jsou standardní – pro konkrétního uživatele a pro všechny uživatele na počítači. Vidíte, že na svém počítači jsem přidal ještě třetí cestu a využívám Dropbox – tím mám zajištěno, že všechny moduly, které potřebuji na svých počítačích, mám stále dostupné. Ukažme si, co obsahuje první adresář:

PS C:\> ls ($env:PSModulePath -split ';')[0] |? {$_.PsIsContainer} | Format-Wide Name -Column 3
Add-on.AuthoringToolkit Add-on.BlueConsole Add-on.ExpandAlias
Add-on.HelpBrowser Add-on.ModuleManagement Add-on.NumberedBookmarks
Add-on.PowerGUIOnline Add-on.ScriptEditorEssentials Add-on.ScriptSigning
Add-on.TestAddOn adoLib Agent
CP2010 DemoPowerShell DotNet
FileSystem ISECreamBasic IsePack
MetaProgramming OracleClient OracleIse
PBM PoShTD PowerBoots
PowerShellPack PowerTab PSCodeGen
Pscx Pscx_old PSImageTools
PSRemoteRegistry PSRSS PSSystemTools
PSUserTools Repl Setup
ShareUtils ShowMbrs SMS2003
SQLIse SQLMaint SQLParser
SQLPSX SQLServer SSIS
TaskScheduler WPK

Poznámka: V tuto chvíli byste již měli rozumět celé konstrukci. Jediný možný zádrhel se vám může stát při zkoumání konstrukce |? {$_.PsIsContainer} – jedná se o filtrování pouze adresářů.

Každý modul je uložen v samostatném adresáři a je tvořen (minimálně) souborem stejného jména. Vytvořme si tedy základ pro náš modul.

PS C:\> mkdir (Join-Path ($env:PSModulePath -split ';')[0] Kurzy)
PS C:\> cd (Join-Path ($env:PSModulePath -split ';')[0] Kurzy)
PS C:\Documents and Settings\moravec\My Documents\WindowsPowerShell\Modules\Kurzy> "" > Kurzy.psm1
PS C:\Documents and Settings\moravec\My Documents\WindowsPowerShell\Modules\Kurzy> Get-Module k* -List
ModuleType Name ExportedCommands

---------- ---- ----------------

Script Kurzy {}

Nejprve jsme vytvořili adresář, poté soubor s příponou PSM1 (PowerShell Module) a pak jsme si ověřili, že náš modul existuje a je „viditelný“. Samozřejmě zatím nic neumí. Přepíšeme tedy náš původní kód do trošku čitelnější podoby a vložíme jej do souboru kurzy.psm1.

function Get-CNBExchangeRate
{
$wc = New-Object Net.WebClient
$link = 'https://www.cnb.cz/cs/financni_trhy/devizovy_trh/kurzy_devizoveho_trhu/denni_kurz.txt'
$file = 'C:\Scripts\kurzy\kurzy.txt'
$wc.DownloadFile($link, $file)
}
function Get-Kurz {
param([string]$kod)
Get-CNBExchangeRate
$file = 'C:\Scripts\kurzy\kurzy.txt'
$pattern = '\|(?<kurz>\d{2}.\d{1,3})$'
gc $file |?
{ $_ -match $kod } |%
{ $_ -match $pattern | Out-Null; [double]$matches.kurz.replace(',', '.') }
}

function eur {Get-Kurz EUR}
function usd {Get-Kurz USD}

Vše jsme rozdělili do dvou hlavních částí (funkcí). První funkce stáhne soubor s kurzy do daného adresáře a druhá provede vlastní konverzi na základě dané měny (určené parametrem $kod). Funkce eur a usd nám slouží pouze pro rychlejší zobrazení požadované hodnoty.

PS C:\Documents and Settings\moravec\My Documents\WindowsPowerShell\Modules\Kurzy> cd c:\
PS C:\> Import-Module Kurzy
PS C:\> usd

19,009

PS C:\> Get-Module k*
ModuleType Name ExportedCommands

---------- ---- ----------------

Script Kurzy {usd, eur, Get-Kurz, Get-CNBExchangeRate}

Po importu modulu můžeme jednoduchým příkazem (funkcí) zobrazit aktuální kurz. Všimněte si, že po dalším volání cmdletu Get-Module vidíme všechny funkce, které jsme vytvořili. Všechny jsou „exportované“ do aktuálního prostředí PowerShellu a můžeme tedy všechny použít. Pro naše potřeby, ale není nutné používat přímo funkci Get-CNBExchangeRate, protože tato funkce je volána uvnitř funkce Get-Kurz vždy, když je aktuálně zapotřebí. Proto využijeme možnost exportovat pouze funkce, které určíme pomocí cmdletu Export-ModuleMember. Jako poslední řádku našeho modulu přidáme

Export-ModuleMember Get-Kurz, eur, usd
modul odebereme, opětovně importujeme a vyzkoušíme, zda se změna projevila.

PS C:\> rmo kurzy; ipmo kurzy; gmo k*
ModuleType Name ExportedCommands

---------- ---- ----------------

Script kurzy {usd, eur, Get-Kurz}

(je to vidět – opravdu mám rád aliasy :) Ve sloupci ExportedCommands nyní chybí „nechtěná“ funkce a pokud byste jí chtěli nyní použít, nebude pro vás dostupná.

Pro rychlou konverzi můžeme použít funkce eur a usd a v případě potřeby konverze jiné měny, můžeme použít funkci Get-Kurz. Možná by se nám ale hodilo, vytvořit si pro tuto funkci alias. To můžeme provést přímo v rámci modulu a tím zajistit, že alias se nám vytvoří při importu modulu. Trochu upravíme poslední přidanou řádku a místo ní vložíme následující tři:

Set-Alias kurz Get-Kurz
Export-ModuleMember -Function Get-Kurz, eur, usd
Export-ModuleMember -Alias kurz
Přidali jsme alias a navíc jej i určili pro export. Vše opět ověříme:

PS C:\> rmo kurzy; ipmo kurzy; gmo k* | fl
Name : kurzy
Path : C:\Documents and Settings\moravec\My Documents\WindowsPowerShell\Modules\kurzy\kurzy.psm1
Description :
ModuleType : Script
Version : 0.0
NestedModules : {}
ExportedFunctions : {eur, Get-Kurz, usd}
ExportedCmdlets : {}
ExportedVariables : {}
ExportedAliases : kurz

Je vidět, že alias se nám opravdu objevil na seznamu. Nyní můžeme využít tento alias pro zjištění kurzu jakékoli jiné měny ze seznamu.

PS C:\> kurz jpy

22,534

Samozřejmě bychom mohli náš modul dále vylepšovat. Nyní nám například funguje pouze pokud nepoužíváme proxy server – v případě, že jej používáme (nebo nejsme připojeni na internet) modul nefunguje, mohli bychom chtít zobrazit seznam možných měn, atd. Pěkné domácí úkoly, že ano?

Závěrem bych chtěl upozornit na jeden užitečný Add-in do PowerGUI Script Editoru. Jmenuje se Module Management a slouží (mimojité) ke konverzi vašich současných skriptů do modulu. Návod na instalaci a použití je dostupný na stránce tohoto Add-inu. Pokud jej budete zkoušet, určitě si všimnete, že kromě souboru s příponou PSM1 (vlastní modul), vytvoří i soubor PSD1. Tento soubor se nazývá manifest a k čemu všemu se dá využít si ukážeme v příští části, kde naše putování po modulech ukončíme. Zároveň si také ukážeme binární moduly a podíváme se na jednu velice užitečnou vlastnost modulů – těšte se :).

- David Moravec