IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 8 - PowerShell - Kolekce objektů, II. část

V minulé lekci, PowerShell - Kolekce objektů, jsme se seznámili s první části PowerShell kolekcemi a vysvětlili jsme si práci s každou z nich.

V dnešní lekci kurzu PowerShell frameworku si dobereme typy kolekcí PowerShellu.

Dynamické pole (System.Collections.ArrayList)

Dynamické pole ArrayList na rozdíl od statického pole Array nepodporuje ochranu datového typu. Nicméně je možné vytvořit dynamické pole pomocí statického pole s ochranou datového typu, viz. příklad níže:

[System.Collections.ArrayList]$Pole=[int[]]@(1,2,3)

Dynamické pole na rozdíl od statického pole (v PowerShellu pod datovým typem [System.Collections.ArrayList]) navíc umožňuje odebrání prvku (elementů) z pole.

Deklarace dynamického pole se provádí podobně jako u statického pole s tím rozdílem, že tento datový typ musíme vynutit vždy, když potřebujeme dynamické pole:

[System.Collections.ArrayList]$Pole=@(1,2,3)

Základní operace s prvky v dynamickém poli

Dynamické pole podporuje všechny operace jako statické pole, navíc ještě však:

  • odebrání prvku z pole - v případě dynamického pole se toto provádí pomocí metod .Remove(), .RemoveAt() a RemoveRange().
  • vymazání všech prvků z pole - toto se provádí pomocí metody .Clear().
  • obrácení pořadí prvků v poli - někdy je potřeba otočit pořadí prvků v poli, tzn. prvek na prvním místě bude poslední a obráceně. Toto se provádí pomocí metody .Reverse().

Příklad odebrání prvků z pole:

[System.Collections.ArrayList]$Pole=1..10
$Pole.Remove(8) #odstraní prvek s hodnotou 8 z pole
$Pole.RemoveAt(0) #odstraní prvek s indexem 0 (pozice v poli 1)
$Pole.RemoveRange(0,4) #odstraní prvky s indexem 0 až 4 (pozice v poli 1 až 5)

Statické pole versus dynamické pole - performance

Když dojde na otázku rychlosti zpracování dat obou datových typů, zde je dynamické pole jasný vítěz. Pojďme se podívat proč. Nejprve však malá ukázka, kde otestujeme rychlost přidání stejného počtu prvků do obou typů polí. K tomuto použijeme následující kousek kódu a příkaz Measure-Command, který měří délku exekuce kódu:

$ArrayList = New-Object -TypeName 'System.Collections.ArrayList'
$Array = @()

Measure-Command {
    for ($i = 0; $i -lt 10000; $i++) {
        $null = $ArrayList.Add("Adding item $i")
    }
}

Measure-Command {
    for ($i = 0; $i -lt 10000; $i++) {
        $Array += "Adding item $i"
    }
}

A zde je výsledek:

Windows PowerShell
Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 0
Milliseconds      : 53
Ticks             : 531792
TotalDays         : 6.155E-07
TotalHours        : 1.4772E-05
TotalMinutes      : 0.00088632
TotalSeconds      : 0.0531792
TotalMilliseconds : 53.1792

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 3
Milliseconds      : 558
Ticks             : 35581670
TotalDays         : 4.11824884259259E-05
TotalHours        : 0.000988379722222222
TotalMinutes      : 0.0593027833333333
TotalSeconds      : 3.558167
TotalMilliseconds : 3558.167

Jak můžeme vidět, v případě typu ArrayList zabralo 53ms přidat všechny prvky do tohoto pole. Nicméně pro typ Array to zabralo několikanásobně víc času, a to konkrétně 3s a 56ms, což je opravdu markantní rozdíl.

Tento rozdíl v performanci je způsoben systémem, jak pracuje statické a dynamické pole při přidávání dalších prvků:

  • statické pole: velikost statického pole je fixní. To lze ověřit pomocí vlastnosti .IsFixedSize. Při přidání dalšího prvku se vezme poslední přidaný prvek a původní obsah pole a toto pole se v paměti vytvoří znovu i s nově přidaným prvkem. Tudíž při každém přidání nového prvku je toto pole prakticky deklarováno v paměti znovu jako nové pole, pokaždé s fixní velikostí.
  • dynamické pole: velikost dynamického pole není fixní, tudíž může v paměti počítače expandovat. Toto lze taktéž zobrazit pomocí vlastnosti .IsFixedSize. Při přidání dalšího prvku do tohoto pole se tento nový prvek přidá do existujícího pole a jeho velikost se v paměti zvětší.

Statické pole versus dynamické pole - závěr

Uvedeme si pár pro a proti použití statického oproti dynamického pole.

Statické pole:

  • + podpora ochrany datového typu
  • + použit jako defaultní datový typ PowerShellu
  • - performance je opravdu pomalá v porovnání s dynamickým polem
  • - nelze odebírat prvky z pole

Dynamické pole:

  • + několikanásobně lepší performance v porovnání se statickým polem
  • + lze odebírat prvky z pole a to hned několika různými způsoby
  • + metoda .Reverse(), která se občas hodí
  • - chybí podpora ochrany datového typu, nicméně toto lze s menším úsilím implementovat

Hash tabulka (hashtable) versus tříděný slovník (OrderedDictionary)

Ať už HashTable nebo OrderedDictionary, oba tyto typy jsou podobné typu pole s tím rozdílem, že struktura prvku v hash tabulce je ve formátu klíč=hodnota klíče (Key=Value).

Defaultně není hash tabulka tříděná, tudíž prvky na výstupu nemusí být v tom pořadí, v jakém byly do této tabulky přidány. Což ovšem nemusí být vždy problém.

K tomu, abychom docílili stejného pořadí na výstupu i na vstupu, použijeme tříděný slovník.

  • HashTable je deklarována pomocí výrazu @{}.
  • OrderedDictionary je deklarován pomocí výrazu [ordered]@{}.

Pro oba typy však platí stejný zápis dat do této tabulky.

HashTable:

$HashTable = @{
    Name = 'Vojtech'
    Surname = 'Kasny'
    Age = 39
}

OrderedDictionary:

$OrderedHashTable = [ordered]@{
    Name = 'Vojtech'
    Surname = 'Kasny'
    Age = 39
}

Podívejme se na výstup proměnné $HashTable, která není tříděná a proměnné OrderedHashTable, která už tříděná je:

Windows PowerShell
$HashTable

Name                           Value
----                           -----
Name                           Vojtech
Age                            39
Surname                        Kasny

$OrderedHashTable

Name                           Value
----                           -----
Name                           Vojtech
Surname                        Kasny
Age                            39

Základní operace s hash tabulkou

Platí pro oba typy, tříděné i netříděné tabulky.

Deklarace

Provádí se pomocí výrazu @{} a může být buď prázdná nebo s definovanými hodnotami.

Prázdná hash tabulka:

$EmptyHashtable = @{}

Definovaná hash tabulka, která je už setříděná:

$CustomHashtable = [ordered]@{
    Color = 'Red'
    Doors = 4
    Manufactured = Get-Date
}

Přidání záznamu

Provádí se pomocí metody .Add('<key>','<value>'):

$CustomHashtable.Add('Engine','Petrol')

Jméno klíče musí být v tabulce unikátní.

Odebrání záznamu

Provádí se pomocí metody .Remove('<key>'):

$CustomHashtable.Remove('Doors')

Využití hash tabulky v praxi

Hash tabulku lze v praxi využít efektivně ke dvěma věcem:

  • k PSCustomObjectu
  • a k tzv. splattingu
PSCustomObject

Hash tabulku použijeme jako vstupní data pro nový custom objekt, se kterým už umíme pracovat:

$obj = New-Object psobject -Property $CustomHashtable

Výstup:

Windows PowerShell
$obj

Color     Manufactured                   Engine
-----     ------------                   ------
Red       9/26/2020 3:29:36 PM           Petrol
Splatting

Tímto označením není myšleno nic jiného, než předání vstupních parametrů příkazu pomocí hash tabulky. Tato tabulka je poté předána danému příkazu pomocí znaku @ (viz. příklad níže). Toto se provádí pro lepší čitelnost kódu a to tehdy, když počet vstupních parametrů na jednom řádku přesáhne únosnou mez.

Co je únosná mez záleží čistě na daném vývojáři. Já osobně mám tuto mez nastavenou ve Visual Studio Code na 160 znaků.

#Tento zápis je sice funkční, nicméně obtížně čitelný
(Get-WmiObject -Query "select WorkingSetSize from win32_process where name='system'" -EnableAllPrivileges -ComputerName $env:COMPUTERNAME -Verbose -ErrorAction Stop -Namespace root/cimv2).WorkingSetSize

#Stejný zápis pomocí tabulky hash (zde stačí netříděná tabulka)
$CommandArguments = @{
    Query = "select WorkingSetSize from win32_process where name='system'"
    EnableAllPrivileges = $true
    ComputerName = $env:COMPUTERNAME
    Namespace = 'root/cimv2'
    Verbose = $true
    ErrorAction = 'Stop'
}

Příkaz Get-WmiObject stačí poté již zavolat a předat argumenty pomocí splattingu:

Windows PowerShell
(Get-WmiObject @CommandArguments).WorkingSetSize

20189184

Nutno ještě zmínit, že všechny vstupní parametry, ať již skriptu či custom funkce, jsou v kódu reprezentovány systémovou proměnnou $PSBoundParameters, která je typu PSBoundParametersDictionary a lze ji využít stejně jako tabulka hash:

function Write-PassedParameters {
    param (
        [parameter(Mandatory)][int[]]$Port,
        $ComputerName=$env:COMPUTERNAME
    )
    Write-Output $PSBoundParameters
}

Výstup:

Windows PowerShell
Write-PassedParameters 1,2,3,4 -ComputerName localhost

Key               Value
---               -----
ComputerName      localhost
Port              {1, 2, 3, 4}

A ještě ukázka jednoduché konverze na datový typ PSCustomObject:

Windows PowerShell
New-Object psobject -Property (Write-PassedParameters 1,2,3,4 -ComputerName localhost)

ComputerName       Port
------------       ----
localhost          {1, 2, 3, 4}

V další lekci, PowerShell - Cykly, se seznámíme s PowerShell cykly.


 

Předchozí článek
PowerShell - Kolekce objektů
Všechny články v sekci
PowerShell
Přeskočit článek
(nedoporučujeme)
PowerShell - Cykly
Článek pro vás napsal Vojtěch Kašný
Avatar
Uživatelské hodnocení:
12 hlasů
Aktivity