Rovnou na začátku říkám, že název článku je trochu zavádějící. Nebudeme se dnes bavit o cmdletu Where-Object, ale o jeho trochu skryté náhradě. Pro použití následujících technik musíte mít nainstalován PowerShell v4.

Před několika dny jsem se vrátil z MVP Summitu v Redmodu, kde jsem strávil několik dní v přítomnosti dalších MVPků z různých produktových skupin. V rámci mé PowerShell oblasti byly nejzajímavější přednášky na témata již existujících funkcionalit. Několik z nich jsme ale dostali rozebrané do naprostého detailu. V rámci prezentace o DSC (Desired State Configuration) – již jsem o tomto tématu psal – jsme se dostali k zajímavé vlastnosti, o kterou bych se s vámi dnes rád podělil.

V rámci konfigurace a výběru jednotlivých nodů můžete určovat, kde se bude nastavení aplikovat. Vezměme si následující příklad:

Configuration CloudService
{
    Node $AllNodes.Where("Role -eq Web").NodeName {
        WindowsFeature IIS
        {
             Ensure = "Present"
             Name = $Node.RolesToBePresent
        }
    }
}

Důležitý je pro nás třetí řádek, kde říkáme, že ze všech dostupných nodů vyberu pomocí where pouze ty, které mají mít roli web a z těchto si zapamatuji jejich jméno. Zajímavá je právě konstrukce .Where(„Role –eq Web“). Tuto konstrukci můžeme použít i v konzoli, např.:

PS C:\> (Get-Process).Where({$_.name -eq "svchost"})

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s)   Id ProcessName
------- ------ ----- ----- ----- ------   -- -----------
    397     14  3576  9984    45   0.27  592     svchost
    327     14  2384  5852    28   0.19  620     svchost
    418     32  9100 13172   638   6.25  672     svchost
    497     21 11368 15796    70   1.08  732     svchost
   1128     45 12876 27032   192   3.56  764     svchost
    462     26  4588 11056    82   0.28  800     svchost
    522     33  6908 15024  1129   0.36  872     svchost
    352     32  8724 10844    55   0.23  988     svchost
    681     26 62800 77052   191   4.95 1224     svchost
    252     15  2512  7340    47   0.06 1324     svchost

Pro porovnání, toto je standardní zápis předchozí konstrukce:

PS C:\> Get-Process | where name -eq 'svchost'
PS C:\> Get-Process | where { $_.name -eq 'svchost' }

První z nich lze použít od verze 3. Jedná se o tzv. zjednodušenou syntaxi. Poslední zápis funguje již od první verze PowerShellu. Jen pro zajímavost. Pokud bych potřeboval předchozí akci provést narychlo v konzoli, asi bych použil následující zápis:

PS C:\> gps|? name -eq svchost

Vidíte, že kolik adminů, tolik různých zápisů. Nicméně …

Na první pohled asi vypadá nová syntaxe trochu divná. V DSC má své opodstatnění, ale v konzoli se jedná o vůbec nejdelší zápis. Výhodou je ovšem rychlost běhu.

Uložíme si všechny tři varianty do proměnné:

PS C:\> $c = '(gps).Where({$_.name -eq "svchost"})','gps | where { $_.name -eq "svchost" }','gps | where name -eq "svchost"'

Nyní si můžeme všechny příkazy spustit:

PS C:\> $c |% { Write-Host $_ -fore Yellow; iex $_ }
(gps).Where({$_.name -eq "svchost"})

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s)   Id ProcessName
------- ------ ----- ----- ----- ------   -- -----------
    394     14  3524  9964    44   0.27  592     svchost
    301     13  2216  5784    26   0.19  620     svchost
    419     32  9072 13176   638   6.28  672     svchost
    483     20 11312 15944    68   1.11  732     svchost
   1112     45 12804 27048   191   3.69  764     svchost
    429     26  4560 10784    81   0.28  800     svchost
    526     33  6964 15048  1130   0.36  872     svchost
    344     32  8692 10788    54   0.23  988     svchost
    677     26 64500 82328   190   8.64 1224     svchost
    252     15  2512  7340    47   0.09 1324     svchost
gps | where { $_.name -eq "svchost" }
    394     14  3524  9964    44   0.27  592     svchost
    301     13  2216  5784    26   0.19  620     svchost
    419     32  9072 13176   638   6.28  672     svchost
    483     20 11312 15944    68   1.11  732     svchost
   1112     45 12804 27048   191   3.69  764     svchost
    429     26  4560 10784    81   0.28  800     svchost
    526     33  6964 15048  1130   0.36  872     svchost
    344     32  8692 10788    54   0.23  988     svchost
    677     28 64500 82328   190   8.66 1224     svchost
    252     15  2512  7340    47   0.09 1324     svchost
g
ps | where name -eq "svchost"
    394     14  3524  9964    44   0.27  592     svchost
    301     13  2216  5784    26   0.19  620     svchost
    419     32  9072 13176   638   6.28  672     svchost
    483     20 11312 15944    68   1.11  732     svchost
   1112     45 12804 27048   191   3.69  764     svchost
    429     26  4560 10784    81   0.28  800     svchost
    526     33  6964 15048  1130   0.36  872     svchost
    344     32  8692 10788    54   0.23  988     svchost
    677     26 64500 82328   190   8.69 1224     svchost
    252     15  2512  7340    47   0.09 1324     svchost

Zde jsem si schválně dovolil použití méně známého aliasu. Iex je alias pro Invoke-Expression. Tento cmdlet je schopen převzít text na vstupu a spustit jej jako příkaz PowerShellu. Ve smyčce jsem tedy prošel všechny tři varianty zápisu a pomocí Invoke-Expression je spustil. Pro oddělení jsem použil Write-Host, abychom viděli, v které fázi se nacházíme.

Nyní si pojďme porovnat rychlost všech tří variant.

PS C:\> $c|% { "{0,-40} : {1}" -f $_, $((Measure-Command { 1..100 |% {iex $_} }).Milliseconds) }
(gps).Where({$_.name -eq "svchost"}) : 46
gps | where { $_.name -eq "svchost" } : 93
gps | where name -eq "svchost" : 128

Možná bychom nejprve mohli rozklíčovat zápis. Opět procházíme všechny varianty, ale jejich výpis nyní zobrazujeme pomocí F operátoru. Pokud o něm chcete vědět víc, můžete se podívat na mé staré články. První část zobrazuje text a druhá vlastní čas běhu. Pro měření běhu jsem využil cmdlet Measure-Command a poté zobrazil pouze jeho vlastnost Milliseconds. Je vidět, že rozdíl v běhu je docela markantní. Největší režie je u posledního zápisu. V případě, že bychom filtrovali nějaké větší pole objektů, může nám nový zápis hodně zkrátit celkový čas skriptu.

Pojďme si tedy ukázat další možnosti daného zápisu. Where můžete řetězit za sebe. Můžete tedy vyzkoušet toto:

PS C:\> (gps).Where({$_.name -eq "svchost"}).where({$_.id -ge 800})

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s)   Id ProcessName
------- ------ ----- ----- ----- ------   -- -----------
    458     26  4856 11300    82   0.28  800     svchost
    519     32  6936 15092  1129   0.38  872     svchost
    348     32  8848 10840    55   0.23  988     svchost
    675     26 64504 82368   190  12.67 1224     svchost
    252     15  2512  7340    47   0.13 1324     svchost

Můžete si nechat vypsat pouze první nebo poslední záznam:

PS C:\> (gps).Where({$_.name -eq "svchost"}, "last")

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s)   Id ProcessName
------- ------ ----- ----- ----- ------   -- -----------
    252     15  2512  7340    47   0.16 1324     svchost

PS C:\> (gps).Where({$_.name -eq "svchost"}, "first")

Handles NPM(K) PM(K) WS(K) VM(M) CPU(s)  Id ProcessName
------- ------ ----- ----- ----- ------  -- -----------
    394     14  3524  9968    44   0.27 592     svchost

Zajímavé je, že ve stejném tvaru můžete použít i foreach. Můžete tedy zkusit:

PS C:\> (gps).Where({$_.name -eq "svchost"}).foreach({$_.name})

svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost
svchost

Kdy pro každý z objektů vypisujeme pouze jeho jméno. Samozřejmě byste asi dokázali vymyslet užitečnější příklad. Zkuste si například vzít některý z vašich posledních použitých příkazů a převést jej na tuto syntaxi. Klidně si i změřte délku běhu.

Dnešním cílem nebylo přesvědčit vás na využívání této syntaxe. Chtěl jsem vám ukázat jednu z možných cest a je na vás, abyste se rozhodli. Určitě je dobré o této variantě vědět a v případě, že budete mít pocit, že by se vám mohla hodit, vyzkoušejte ji. Nebo budete alespoň vědět, že jste to již někde viděli, pokud se v budoucnu dostanete k článku, kde bude tento zápis použit.

Ještě jednou připomínám, že pro možnost použití tohoto tvaru, musíte mít instalován PowerShell v4.

David Moravec, MVP
Mainstream Technologies