V dnešním povídání o PowerShellu představím modul PSCX, neboli PowerShell Community Extensions. Na jeho vývoji se podílí jedni z mála vývojářských PowerShell MVP, proto se dá čekat jiné zaměření, než u většiny PowerShell modulů bývá běžné.
Modul lze stáhnout na adrese http://pscx.codeplex.com. V release notes se detailně popisuje, jak modul zprovoznit. Stručně řečeno stačí pouze odblokovat stáhnutý zip soubor (pokud jste stahovali pomocí Internet Exploreru; ostatní prohlížeče informaci o stáhnutí nenastavují), anebo nastavit policy na Unrestricted a je hotovo. Obsah zip souboru se pak rozbalí na cestu z proměnné prostředí $env:PsModulePath. Pokud takovou proměnnou prostředí ještě nemáte, je potřeba ji vytvořit. Jednou z rozumných hodnot je do C:\Windows\system32\WindowsPowerShell\v1.0\Modules\. V tomto se postup neliší od "instalace" jakéhokoliv jiného modulu.

Import modulu

Po nainstalování se modul jednoduše naimportuje příkazem Import-Module pscx (anebo aliasem ipmo). Pokud chcete vidět, které všechny příkazy (cmdlet, funkce, aliasy) byly naimportovány, pomůže přepínač -verbose, případně lze později příkazy získat pomocí Get-Command:

PS> ipmo pscx -Verbose -Force
VERBOSE: Loading module from path 'C:\Users\stej\Documents\WindowsPowerShell\Modules\pscx\pscx.ps...
VERBOSE: Loading 'Assembly' from path 'C:\Users\stej\Documents\WindowsPowerShell\Modules\pscx\Psc...
VERBOSE: Loading 'TypesToProcess' from path 'C:\Users\stej\Documents\WindowsPowerShell\Modules\ps...
...
VERBOSE: Loading module from path 'C:\Users\stej\Documents\WindowsPowerShell\Modules\pscx\Pscx.dll'.
VERBOSE: Importing cmdlet 'Out-Clipboard'.
VERBOSE: Importing cmdlet 'Expand-Archive'.
...
VERBOSE: Importing function 'Show-Tree'.
VERBOSE: Importing function 'Stop-RemoteProcess'.
VERBOSE: Importing alias '?:'.
VERBOSE: Importing alias '??'.
VERBOSE: Importing alias 'call'.
...

PS> > Get-Command -module pscx
CommandType     Name                  Definition
-----------     ----                  ----------
Alias           ?:                    Invoke-Ternary
Alias           ??                    Invoke-NullCoalescing
Function        Add-DirectoryLength   ...
Cmdlet          Add-PathVariable      Add-PathVariable [-Value]  [-Name ] [-Prepend... Function Add-ShortPath ... ...

Příkazů naimportovaných z PSCX modulu je velké množství. Pro lepší orientaci a představu můžeme zkusit seskupit příkazy podle podstatného jména:

PS> > Get-Command -module pscx | 
  group -prop { $_.Name -replace '^.*?-',''} | 
  sort count -desc

Count Name                  Group
----- ----                  -----
    6 MSMQueue              {Clear-MSMQueue, Get-MSMQueue, New-MSMQueue, Receive-MSMQueue...}
    4 Clipboard             {Get-Clipboard, Out-Clipboard, Set-Clipboard, Write-Clipboard}
    3 Xml                   {Convert-Xml, Format-Xml, Test-Xml}
    3 TerminalSession       {Disconnect-TerminalSession, Get-TerminalSession, Stop-TerminalSession}
    3 Bitmap                {Export-Bitmap, Import-Bitmap, Resize-Bitmap}
    3 AlternateDataStream   {Get-AlternateDataStream, Remove-AlternateDataStream, Test-AlternateDataStream}
    3 PathVariable          {Add-PathVariable, Get-PathVariable, Set-PathVariable}
    3 EnvironmentBlock      {Get-EnvironmentBlock, Pop-EnvironmentBlock, Push-EnvironmentBlock}
    2 ForegroundWindow      {Get-ForegroundWindow, Set-ForegroundWindow}
    2 File                  {Edit-File, Unblock-File}
...

V tomto krátkém seznámení není prostor na podrobné představení jednotlivých příkazů. Z toho důvodu v následujících odstavcích vyberu pouze některé. Pořád ale platí, že PSCX je modul plný perliček!

Ternární operátor a podobné pomůcky.

Také jste už narazili na konstrukce jako jsou tyto následující?

$a = if ($somevar) { 'set' } else { 'unset' }
# anebo
if ($somevar) { 
	$a = 'set' 
} else {
	$a 'unset' 
}

A také jste si vzpomněli na ternární operátor známý z C#? Pak vás bude zajímat funkce Invoke-Ternary, nebo ještě lépe alias ?:. Zápis je kratší a čitelnější.

$a = ?: { $somevar } { 'set' } { 'unset' }
$a = ?: { gci d:\temp } { 'something in temp' } { 'temp empty' }

"Operátor" ovšem nejde řetězit. Případné další vyhodnocování je tak nutné uzavřít do posledního else bloku.

?: { gci d:\temp\ } `
  { 'something in temp' } `
  { ?: { gci d:\temp2} `
    { 'something in temp2' } `
    { 'even temp2 is empty'} }

Podobně vývojářský orientovaný je cmdlet Invoke-NullCoalescing, ke kterému existuje alias ??. Podle jména aliasu se dá zřejmě vytušit, k čemu se takový cmdlet dá použít.

PS> ?? { $Env:PSModulePath } { "$psHome\modules" }
PS> ?? { $Env:PSModulePath } { throw '$env:PsModulePath does not exist' }

Je dobré si uvědomit, že oba příkazy pracují se scriptblocky. Šlo by sice definovat příkazy tak, aby například fungovalo volání ?: { gci d:\temp } 'something in temp' 'temp empty' (tj. poslední dva parametry jsou předány standardně hodnotou), ale tím by se ztratila flexibilita poskytovaná scriptblockem.

Poslední příkaz orientovaný převážně na vývojáře se jmenuje Invoke-Method s aliasem call. Ten najde uplatnění v okamžiku, kdy pracujeme s kolekcí objektů a na každém z nich potřebujeme zavolat nějakou metodu.

PS> $o = [datetime]'2012-01-01', [datetime]'2012-01-02', [datetime]'2012-01-03'
PS> $methods = 'AddHours', 'AddMinutes', 'AddSeconds'
PS> foreach ($m in $methods) {
>>  $o | Invoke-Method $m ([double]1.0)
>>}

1. ledna 2012 1:00:00
2. ledna 2012 1:00:00
3. ledna 2012 1:00:00
1. ledna 2012 0:01:00
2. ledna 2012 0:01:00
3. ledna 2012 0:01:00
1. ledna 2012 0:00:01
2. ledna 2012 0:00:01
3. ledna 2012 0:00:01

Všimněte si přetypování na [double]. Funkce Invoke-Method není tak inteligentní, že by konverzi typů řešila za nás. V tomto případě by to možné bylo, ale obecně bychom se mohli dočkat nečekaných překvapení.

Stejného efektu jako u Invoke-Method se dá samozřejmě dosáhnout i pomocí ForEach-Object, ale už musíme znát jméno metody:

$methods = 'AddHours', 'AddMinutes', 'AddSeconds'
PS> foreach ($m in $methods) {
>>  $o | % { $_."$m"(1.0)  # chyba
>>}
Unexpected token '(' in expression or statement.
...

PS> $o | % { $_.AddHours(1) } # ok

Podobný příkaz pro property nebyl potřeba, protože u properties toto funguje samo:

PS> foreach ($m in 'year', 'month', 'day') {
>> $o | % { $_."$m" }
>> }

2012
2012
2012
1
1
1
1
2
3

Mezi další vývojářské příkazy se řadí například také Get-LoremIpsum, Invoke-GC (invoke garbage collector), Invoke-Reflector (již mírně neaktuální, že?), Invoke-Apartment (spuštění PowerShell kódu v daném apartmentu – STA/MTA).

Formátování

Do této skupiny zahrnu dva příkazy, které čas od času mohou pomoci. První naformátuje xml vstup, druhý pak zobrazí obsah adresáře ve stromové struktuře (nemusí ale jít jen o adresář na disku).

První příkaz Format-Xml zobrazí obsah XML souboru pěkně naformátovaný.

PS> $xml = [xml]('<root><post id="1" date="sun">first post</post>' +
  '<post id="2" date="mon">first post</post></root>')
PS> $xml | Format-Xml -AttributesOnNewLine

<root>
  <post
    id="1"
    date="sun">first post</post>
  <post
    id="2"
    date="mon">first post</post>
</root>

Format-Xml umí zpracovat a naformátovat i soubor, pokud mu jako argument podáme cestu na disk.
Druhý příkaz jménem Show-Tree bude nejlepší také rovnou ukázat na příkladu.

PS> Show-Tree G:\Programs\IrfanView425\
G:\Programs\IrfanView425\
|--Html
|--Languages
|--Plugins
|  |--Adobe 8BF
|  |--Crw
|  |--Ecw
|  |--Filter Factory 8BF
|  \--Fmod
\--Toolbars

Pokud nespecifikujeme jinak, příkaz zobrazí pouze adresáře (obecně kontejnery). Příkaz má samozřejmě sadu přepínačů, z nichž pro nás nejzajímavější budou -ShowProperty a -ShowLeaf.

PS> Show-Tree G:\Programs\IrfanView425\Plugins -ShowLeaf #zobrazi soubory

G:\Programs\IrfanView425\Plugins
|--Adobe 8BF
|  |--HarrysFilters.8bf
|  \--PopArt.8bf
|--Crw
|  \--Readme_Canon.txt
|--Ecw
|  |--NCScnet.dll
|  |--NCSEcw.dll
|  |--NCSEcwC.dll
|  \--NCSUtil.dll

# zobrazi soubory a property k souborum a adresarum
PS>  Show-Tree G:\Programs\IrfanView425\Plugins\Crw -ShowProperty -ShowLeaf
G:\Programs\IrfanView425\Plugins\Crw
|--Property: Attributes = Directory, Archive
|--Property: CreationTime = 11/30/2010 19:04:58
|--Property: LastAccessTime = 11/30/2010 00:00:00
|--Property: LastWriteTime = 11/30/2010 19:05:00
|--Property: Mode = da---
...
\--Readme_Canon.txt
   |--Property: Attributes = Archive
   |--Property: CreationTime = 11/30/2010 19:04:58
   |--Property: Directory = G:\Programs\IrfanView425\Plugins\Crw
    ...

Show-Tree tedy názorně zobrazí adresářovou strukturu včetně properties jednotlivých položek. Cesta ovšem může ukazovat i jinam než na disk. Jinak řečeno můžeme použít cestu z jakéhokoliv providera. Co třeba udělá Show-Tree cert: -ShowLeaf? Anebo

PS> Show-Tree 'HKCU:\Software\Microsoft\Internet Explorer\main' -ShowProperty
HKCU:\Software\Microsoft\Internet Explorer\main
|--Property: Anchor Underline = yes
|--Property: AutoHide = yes
|--Property: Cache_Update_Frequency = Once_Per_Session
|--Property: CompatibilityFlags = 0
|--Property: Disable Script Debugger = yes
|--Property: DisableScriptDebuggerIE = yes
...
\--WindowsSearch
   |--Property: Cleared = 1
   |--Property: Cleared_TIMESTAMP = ...
   \--Property: Version = WS not running

V tomto okamžiku je jasné, že na rychlé prozkoumání registrů se tento příkaz velmi dobře hodí. Najednou je schopen zobrazit informace o struktuře i o hodnotách.

Jen pro úplnost musím zmínit, že máme k dispozici i hexa formátování díky příkazu Format-Hex.

CD

Jeden z užitečných příkazů, který přetěžuje původní funkci, se skrývá za aliasem CD. Příkaz provede změnu adresáře, jak by každý čekal. Krom toho si ale vnitřně udržuje i seznam již navštívených adresářů a dá se v tomto seznamu i pohybovat.

PS> cd hkcu:\Software\Microsoft\VisualStudio\10.0\FileMRUList
PS> cd HKCU:\Software\7-ZIP
PS> cd d:\temp
PS>$pwd
Path
----
D:\temp

PS> cd

     # Directory Stack:
   --- ----------------
     0 C:\prgs\tools\Console2
     1 HKCU:\Software\Microsoft\VisualStudio\10.0\FileMRUList
     2 HKCU:\Software\7-ZIP
->   3 D:\temp

Prosté vypsání seznamu tedy dosáhneme pomocí cd bez argumentů. Pro pohyb zpět použijeme cd -, pro pohyb vpřed pak cd +. Pokud známe konkrétní číslo, dostaneme se na něj pomocí cd -cislo

PS> cd -
PS>  cd

     # Directory Stack:
   --- ----------------
     0 C:\prgs\tools\Console2
     1 HKCU:\Software\Microsoft\VisualStudio\10.0\FileMRUList
->   2 HKCU:\Software\7-ZIP
     3 D:\temp
PS> cd -0; $pwd  # skok na první záznam
Path
----
C:\prgs\tools\Console2

Druhou drobnou vychytávkou je posun v adresářové struktuře směrem k rootu, ale o více parentů. O kolik přesně to je, to záleží na počtu teček. Posun o jednoho parenta značí dvě tečky. O dva parenty tři tečky atd.

PS> cd HKCU:\Software\Microsoft\VisualStudio\10.0\FileMRUList
PS> cd .....; $pwd
Path
----
HKCU:\Software

Blbinka

Nevím, jestli tato funkce má nějaký praktický význam, ale autoři ji do modulu zařadili. Její jméno je Out-Speech. Jde o funkci, která předčítá daný text. Jak by tedy mohlo vypadat pročtení Hamleta před spaním?

PS> $url = 'http://www.gutenberg.org/files/27761/27761-0.txt'
PS> (new-object Net.WebClient).downloadString() | out-Speech

Pro dnes vše

Příště bych mohl představit ještě alespoň jednu sadu příkazů. Těžko se mi do dnešního dílu vybíralo, nabízelo se hodně možností. Alespoň jeden článek se tedy pravděpodobně bude věnovat PSCX a dalším zajímavým příkazům.

Čím jiným se rozloučit před Vánoci než 'merry christmas and happy new year' | out-speech (česky mu to bohužel nejde).

- Josef Štefan