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í wherepouze 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
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 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.