Minden, amit tudni akart a hash táblákról

Szeretnék egy lépést hátra, és beszélni kivonatolók. Folyamatosan használom őket. A tegnap esti felhasználói csoporttalálkozónk után tanítottam róluk valakit, és rájöttem, hogy ugyanolyan zavarban voltam velük, mint ő. A hash-táblák nagyon fontosak a PowerShellben, ezért jó, ha alaposan ismerjük őket.

Megjegyzés

A cikk eredeti verziója@KevinMarquette blogján jelent meg. A PowerShell csapata köszönjük Kevinnek, hogy megosztotta velünk ezt a tartalmat. Kérjük, látogasd meg a blogot a PowerShellExplained.comoldalon.

Hasítótábla mint dolgok gyűjteménye

Szeretném, ha először egy hash táblára gyűjteményként tekintenétek a hash tábla hagyományos definíciója szerint. Ez a definíció alapvető ismereteket nyújt arról, hogyan működnek, amikor később fejlettebb dolgokhoz használják őket. Ennek a megértésnek a kihagyása gyakran zavart okoz.

Mi az a tömb?

Mielőtt belevágnék abba, hogy mi is az a hash tábla, először meg kell említenem a tömböket. Ennek a megbeszélésnek a céljából, egy tömb egy értékek vagy objektumok listája vagy gyűjteménye.

$array = @(1,2,3,5,7,11)

Ha az elemeket egy tömbbe illeszti, a foreach használatával iterálhat a listában, vagy index használatával hozzáférhet a tömb egyes elemeihez.

foreach($item in $array)
{
    Write-Output $item
}

Write-Output $array[3]

Az értékeket az indexek használatával is ugyanúgy frissítheti.

$array[2] = 13

Csak felületesen megismertem a tömböket, de ez segít majd kontextusba helyezni őket, amikor áttérek a hashtáblákra.

Mi az a kivonatoló?

Az illesztőtáblák alapszintű technikai leírásával kezdem, mielőtt rátérnék a PowerShell más felhasználási módjaira.

A hashtábla egy tömbhöz hasonló adatstruktúra, azzal a különbséggel, hogy az egyes értékeket (objektumokat) kulcs használatával tárolod. Ez egy alapkulcs-/értéktároló. Először létrehozunk egy üres hashtáblát.

$ageList = @{}

Figyelje meg, hogy kapcsos zárójelek használatosak a hash tábla definiálására, nem pedig zárójelek. Ezután hozzáadunk egy elemet az alábbihoz hasonló kulccsal:

$key = 'Kevin'
$value = 36
$ageList.Add( $key, $value )

$ageList.Add( 'Alex', 9 )

A személy neve a kulcs, és az életkora az az érték, amelyet menteni szeretnék.

A zárójelek használata a hozzáféréshez

Miután hozzáadta az értékeket a kivonatolóhoz, ugyanezzel a kulccsal húzhatja vissza őket (a tömbhöz hasonló numerikus index helyett).

$ageList['Kevin']
$ageList['Alex']

Ha meg akarom tudni Kevin korát, az ő nevével férek hozzá. Ezzel a módszerrel értékeket is hozzáadhatunk vagy frissíthetünk a hashtáblában. Ez ugyanúgy működik, mint a fenti Add() metódus.

$ageList = @{}

$key = 'Kevin'
$value = 36
$ageList[$key] = $value

$ageList['Alex'] = 9

Van egy másik szintaxis, amellyel elérheti és frissítheti azokat az értékeket, amelyeket egy későbbi szakaszban fogok tárgyalni. Ha egy másik programozási nyelvről lép át a PowerShellbe, ezek a példák illeszkedni fognak ahhoz, ahogyan korábban a hashtáblákat használhatta.

Hash táblák létrehozása értékekkel

Eddig egy üres hasítótáblát hoztam létre ezekhez a példákhoz. A kulcsokat és értékeket a létrehozásukkor előre feltöltheti.

$ageList = @{
    Kevin = 36
    Alex  = 9
}

Keresési táblaként

Az ilyen típusú hash-tábla valódi értéke, hogy keresési táblaként használhatja. Íme egy egyszerű példa.

$environments = @{
    Prod = 'SrvProd05'
    QA   = 'SrvQA02'
    Dev  = 'SrvDev12'
}

$server = $environments[$env]

Ebben a példában egy környezetet ad meg a $env változóhoz, és a megfelelő kiszolgálót választja ki. Alkalmazhat egy switch($env){...}-t egy ilyen kijelöléshez, de a hash tábla is egy jó lehetőség.

Ez még jobb lesz, ha dinamikusan hozza létre a keresési táblát, hogy később használhassa. Gondolja át ennek a megközelítésnek az alkalmazását, amikor valamit kereszthivatkozni kell. Azt hiszem, ezt még jobban látnánk, ha a PowerShell nem lenne olyan jó a csővezetéken keresztüli szűrésben Where-Object. Ha valaha is olyan helyzetben van, ahol a teljesítmény számít, ezt a megközelítést figyelembe kell venni.

Nem mondom, hogy a gyorsabb, de ez megfelel az alábbi szabálynak: Ha számít a teljesítmény, végezzen tesztet.

Többszörös kijelölés

Általában a hashtáblákat kulcs/érték párosként kezeljük, ahol megad egy kulcsot, és kap egy értéket. A PowerShell lehetővé teszi a kulcsok tömbjének megadását több érték lekéréséhez.

$environments[@('QA','DEV')]
$environments[('QA','DEV')]
$environments['QA','DEV']

Ebben a példában ugyanazt a fent említett keresési hashtáblát használom, és három különböző tömbstílust mutatok be az egyezések megkereséséhez. Ez egy rejtett gyöngyszem a PowerShellben, amelyről a legtöbb ember nem tud.

Hash táblák iterálása

Mivel a hash-tábla egy kulcs/érték párok gyűjteménye, másképp kell iterálni rajta, mint egy tömbön vagy egy hagyományos elemlistán.

Az első dolog, amit észre kell venni, hogy ha a hash táblát csővezetékkel továbbítja, a csővezeték egy objektumként kezeli azt.

PS> $ageList | Measure-Object
count : 1

Annak ellenére, hogy a Count tulajdonság azt jelzi, hogy hány értéket tartalmaz.

PS> $ageList.Count
2

Ha csak az értékekre van szüksége, a Values tulajdonság használatával megkerülheti ezt a problémát.

PS> $ageList.Values | Measure-Object -Average
Count   : 2
Average : 22.5

Gyakran hasznosabb a kulcsok számbavétele és használata az értékek eléréséhez.

PS> $ageList.Keys | ForEach-Object{
    $message = '{0} is {1} years old!' -f $_, $ageList[$_]
    Write-Output $message
}
Kevin is 36 years old
Alex is 9 years old

Ugyanez a példa egy foreach(){...} hurokkal.

foreach($key in $ageList.Keys)
{
    $message = '{0} is {1} years old' -f $key, $ageList[$key]
    Write-Output $message
}

A hash-táblában végigmegyünk minden kulcson, és azt használjuk az érték eléréséhez. Ez egy gyakori minta a hasítótáblák kollekcióként való használatakor.

GetEnumerator()

Ez elvezet minket a hash táblán való iteráláshoz GetEnumerator()-ra.

$ageList.GetEnumerator() | ForEach-Object{
    $message = '{0} is {1} years old!' -f $_.Key, $_.Value
    Write-Output $message
}

Az enumerátor egymás után adja meg az egyes kulcs-/értékpárokat. Kifejezetten erre a használati esetre tervezték. Köszönöm Mark Kraus, hogy emlékeztetett erre.

HibásFelsorolás

Az egyik fontos részlet az, hogy felsorolás közben nem lehet módosítani a hashtáblát. Ha az alapszintű $environments példával kezdjük:

$environments = @{
    Prod = 'SrvProd05'
    QA   = 'SrvQA02'
    Dev  = 'SrvDev12'
}

És ha minden kulcsot ugyanarra a kiszolgálóértékre próbál beállítani, az sikertelen lesz.

$environments.Keys | ForEach-Object {
    $environments[$_] = 'SrvDev03'
}

An error occurred while enumerating through a collection: Collection was modified;
enumeration operation may not execute.
+ CategoryInfo          : InvalidOperation: tableEnumerator:HashtableEnumerator) [],
 RuntimeException
+ FullyQualifiedErrorId : BadEnumeration

Ez is kudarcot vall, hiába tűnik úgy, hogy rendben lesz.

foreach($key in $environments.Keys) {
    $environments[$key] = 'SrvDev03'
}

Collection was modified; enumeration operation may not execute.
    + CategoryInfo          : OperationStopped: (:) [], InvalidOperationException
    + FullyQualifiedErrorId : System.InvalidOperationException

Ebben a helyzetben az a trükk, hogy klónozza a kulcsokat az enumerálás előtt.

$environments.Keys.Clone() | ForEach-Object {
    $environments[$_] = 'SrvDev03'
}

Megjegyzés

Egyetlen kulcsot tartalmazó hashtáblát nem lehet klónozni. A PowerShell hibát jelez. Ehelyett tömbté konvertálja a Kulcsok tulajdonságot, majd iterálja át a tömböt.

@($environments.Keys) | ForEach-Object {
    $environments[$_] = 'SrvDev03'
}

Hashtable mint tulajdonsággyűjtemény

Eddig a hashtable-ban elhelyezett objektumok típusa azonos típusú objektum volt. Az összes példában korokat használtam, és a kulcs a személy neve volt. Ez egy nagyszerű módja annak, hogy megértsük, amikor az objektumgyűjteményben lévő tárgyak mindegyikének van neve. PowerShell-ben a hashtáblák használatának egy másik gyakori módja, ha olyan tulajdonsággyűjteményt tárolunk, ahol a kulcs a tulajdonság neve. Ebben a következő példában ezt az ötletet mutatom be.

Tulajdonságalapú hozzáférés

A tulajdonságalapú hozzáférés használata megváltoztatja a hash-táblák dinamikáját, és azt, hogy hogyan használhatja őket a PowerShellben. Íme a fenti szokásos példa, amely tulajdonságokként kezeli a kulcsokat.

$ageList = @{}
$ageList.Kevin = 35
$ageList.Alex = 9

A fenti példákhoz hasonlóan ez a példa is hozzáadja ezeket a kulcsokat, ha még nem léteznek a kivonatolóban. Attól függően, hogy hogyan definiálta a kulcsokat és az értékeket, ez vagy egy kicsit furcsa, vagy tökéletes. A korlista példája eddig kedvező eredménnyel működött. Ehhez egy új példára van szükségünk, hogy a jövőben megfelelően haladjunk tovább.

$person = @{
    name = 'Kevin'
    age  = 36
}

És ilyen módon adhatunk hozzá attribútumokat a $person-hez, és érhetjük el őket.

$person.city = 'Austin'
$person.state = 'TX'

Hirtelen ez a hash-tábla úgy kezd viselkedni és működni, mint egy objektum. Ez még mindig a dolgok gyűjteménye, így a fenti példák továbbra is érvényesek. Csak más szemszögből közelítjük meg.

Kulcsok és értékek keresése

A legtöbb esetben egyszerűen tesztelheti az értéket az alábbihoz hasonlóval:

if( $person.age ){...}

Egyszerű, de sok hiba forrása volt számomra, mert figyelmen kívül hagytam a logikám egyik fontos részletét. Elkezdtem használni, hogy tesztelje, van-e kulcs. Ha az érték $false vagy nulla volt, az utasítás váratlanul $false ad vissza.

if( $null -ne $person.age ){...}

Ez megkerüli ezt a problémát nulla értékek esetén, de nem $null és nem létező kulcsok esetében. Legtöbbször nem kell ezt a különbséget tennie, de vannak módszerek, amikor igen.

if( $person.ContainsKey('age') ){...}

Egy ContainsValue() is rendelkezünk arra a helyzetre, amikor a kulcs ismerete vagy a teljes gyűjtemény iterálása nélkül kell tesztelnie egy értéket.

Kulcsok eltávolítása és törlése

A Remove() metódussal eltávolíthatja a kulcsokat.

$person.Remove('age')

Ha $null értéket rendel hozzájuk, csak egy $null értékkel rendelkező kulcs marad nálad.

A hash tábla törlésének gyakori módja, ha egyszerűen inicializáljuk azt egy üres hash táblára.

$person = @{}

Bár ez működik, próbálja meg inkább a Clear() metódust használni.

$person.Clear()

Ez egyike azoknak az eseteknek, amikor a módszer önmagát dokumentáló kódot hoz létre, és a kód szándékait nagyon tisztává teszi.

Minden szórakoztató dolog

Rendezett kivonatolók

Alapértelmezés szerint a Hash-táblák nincsenek rendezve. A hagyományos környezetben a sorrend nem számít, ha mindig egy kulcsot használ az értékek eléréséhez. Előfordulhat, hogy azt szeretné, hogy a tulajdonságok a megadott sorrendben maradjanak. Szerencsére van mód erre a ordered kulcsszóval.

$person = [ordered]@{
    name = 'Kevin'
    age  = 36
}

A kulcsok és értékek számbavétele után ezek ebben a sorrendben maradnak.

Beágyazott kivonatolók

Amikor egy hash táblát egy sorban határoz meg, pontosvesszővel elválaszthatja a kulcs-érték párokat.

$person = @{ name = 'kevin'; age = 36; }

Ez hasznos lesz, ha a csőben hozza létre őket.

Egyéni kifejezések a gyakori folyamatparancsokban

Van néhány parancsmag, amely támogatja a hash-táblák használatát egyéni vagy számított tulajdonságok létrehozásához. Ezt gyakran látja Select-Object és Format-Tableesetén. A szóközszótárak egy speciális szintaxissal rendelkeznek, amely így néz ki teljesen kibontva.

$property = @{
    Name = 'TotalSpaceGB'
    Expression = { ($_.Used + $_.Free) / 1GB }
}

A parancsmag Name-val címkézné az oszlopot. A Expression egy szkriptblokk, amely akkor lesz végrehajtva, ha $_ a csőben lévő objektum értéke. Így néz ki a szkript működés közben:

$drives = Get-PSDrive | where Used
$drives | Select-Object -Property Name, $property

Name     TotalSpaceGB
----     ------------
C    238.472652435303

Egy változóban helyeztem el, de könnyen definiálható közvetlenül, és lerövidítheti Name-t n-re és Expression-t e-ra, amikor ebben a kódrészletben dolgozik.

$drives | Select-Object -Property Name, @{n='TotalSpaceGB';e={($_.Used + $_.Free) / 1GB}}

Én személy szerint nem szeretem, hogy milyen hosszúvá teszi a parancsokat, és ez gyakran elősegít néhány rossz viselkedést, amelyekbe nem szeretnék belemenni. Valószínűbb, hogy létrehozok egy új hash táblát vagy pscustomobject-t, az összes kívánt mezőkkel és tulajdonságokkal, ahelyett hogy ezt a megközelítést alkalmaznám a szkriptekben. De van egy csomó kód, ami ezt teszi, ezért azt akartam, hogy tudd. Később egy pscustomobject létrehozásáról beszélek.

Egyéni rendezési kifejezés

A gyűjtemények könnyen rendezhetők, ha az objektumok rendelkeznek a rendezni kívánt adatokkal. Rendezés előtt vagy hozzáadhatja az adatokat az objektumhoz, vagy létrehozhat egy egyéni kifejezést a Sort-Objectszámára.

Get-ADUser | Sort-Object -Property @{ e={ Get-TotalSales $_.Name } }

Ebben a példában a felhasználók listáját készítem el, és néhány egyéni parancsmagot használok, hogy csak a rendezéshez szerezzek be további információkat.

Hashtáblák listájának rendezése

Ha rendelkezik a rendezni kívánt kivonatolók listájával, azt fogja tapasztalni, hogy a Sort-Object nem kezeli a kulcsokat tulajdonságokként. Ezt úgy kerülhetjük meg, hogy egy egyéni rendezési kifejezést használunk.

$data = @(
    @{name='a'}
    @{name='c'}
    @{name='e'}
    @{name='f'}
    @{name='d'}
    @{name='b'}
)

$data | Sort-Object -Property @{e={$_.name}}

A hashtáblák splattelése cmdletekkel

Ez az egyik kedvenc dologom a hash-táblákkal kapcsolatban, amit sokan nem fedeznek fel korán. Az ötlet az, hogy ahelyett, hogy minden tulajdonságot egy sorban megadnál egy parancsmagnak, először elhelyezheti őket egy hash táblában. Ezután speciális módon adhatja át a hash táblát a függvénynek. Íme egy példa a DHCP-hatókör normál módon történő létrehozására.

Add-DhcpServerV4Scope -Name 'TestNetwork' -StartRange '10.0.0.2' -EndRange '10.0.0.254' -SubnetMask '255.255.255.0' -Description 'Network for testlab A' -LeaseDuration (New-TimeSpan -Days 8) -Type "Both"

A splattinghasználata nélkül ezeket a dolgokat egyetlen sorban kell meghatározni. Vagy legördül a képernyőről, vagy ott tördel, ahol éppen úgy érzi. Hasonlítsa össze ezt egy splatting technikát használó paranccsal.

$DHCPScope = @{
    Name          = 'TestNetwork'
    StartRange    = '10.0.0.2'
    EndRange      = '10.0.0.254'
    SubnetMask    = '255.255.255.0'
    Description   = 'Network for testlab A'
    LeaseDuration = (New-TimeSpan -Days 8)
    Type          = "Both"
}
Add-DhcpServerV4Scope @DHCPScope

A @ helyett a $ jel használata hívja meg az splat műveletet.

Csak szánjon egy kis időt, hogy értékelje, milyen könnyen olvasható ez a példa. Pontosan ugyanaz a parancs, ugyanazokkal az értékekkel. Könnyebb megérteni és a jövőben fenntartani a másodikat.

Splatting technikát használok, amikor a parancs túl hosszúvá válik. "Túl hosszúnak azt definiálom, ha az ablakom jobbra kell görgetni." Ha három tulajdonság esetén használok egy függvényt, akkor valószínű, hogy átírom azt egy szórt hashtable-lal.

Splatting opcionális paraméterekhez

A splattinget leggyakrabban azért használom, hogy kezeljem azokat az opcionális paramétereket, amelyek valahonnan máshonnan származnak a szkriptemben. Tegyük fel, hogy van egy függvényem, amely egy Get-CimInstance hívást tördel, amely opcionális $Credential argumentumot használ.

$CIMParams = @{
    ClassName = 'Win32_BIOS'
    ComputerName = $ComputerName
}

if($Credential)
{
    $CIMParams.Credential = $Credential
}

Get-CimInstance @CIMParams

Először létrehozom a hash táblát az általános paraméterekkel. Ezután hozzáadom a $Credential, ha létezik. Mivel itt a splattingot használom, csak egyszer kell meghívnom a Get-CimInstance-t a kódomban. Ez a tervezési minta nagyon tiszta, és sok opcionális paramétert könnyen kezelhet.

A korrektség kedvéért megírhatná a parancsokat, hogy lehetővé tegye a $null értékek használatát a paraméterekhez. Nem mindig tudja irányítani a többi parancsot, amiket használ.

Több léc

Több hash-táblát is átadhat ugyanahhoz a parancsmaghoz. Ha újra megvizsgáljuk az eredeti splatting-példát:

$Common = @{
    SubnetMask  = '255.255.255.0'
    LeaseDuration = (New-TimeSpan -Days 8)
    Type = "Both"
}

$DHCPScope = @{
    Name        = 'TestNetwork'
    StartRange  = '10.0.0.2'
    EndRange    = '10.0.0.254'
    Description = 'Network for testlab A'
}

Add-DhcpServerv4Scope @DHCPScope @Common

Ezt a módszert akkor fogom használni, ha olyan paraméterekkel rendelkezem, amelyeket sok parancsnak adok át.

Splatting a tiszta kód érdekében

Nincs semmi baj azzal, ha egyetlen paramétert szúr be, ha a kód tisztább.

$log = @{Path = '.\logfile.log'}
Add-Content "logging this command" @log

Végrehajtható fájlok felbontása

A splatting néhány olyan végrehajtható fájlon is működik, amelyek /param:value szintaxist használnak. Robocopy.exepéldául ilyen paraméterekkel rendelkezik.

$robo = @{R=1;W=1;MT=8}
robocopy source destination @robo

Nem tudom, hogy ez minden olyan hasznos, de érdekesnek találtam.

Hash táblák hozzáadása

A hash-táblák támogatják az összeadás operátort két hash-tábla összekapcsolásához.

$person += @{Zip = '78701'}

Ez csak akkor működik, ha a két hash tábla nem osztozik kulcson.

Beágyazott hash-táblák

A hash-táblákat értékként használhatjuk egy hash-táblában.

$person = @{
    name = 'Kevin'
    age  = 36
}
$person.location = @{}
$person.location.city = 'Austin'
$person.location.state = 'TX'

Egy alapvető hash táblával kezdtem, amely két kulcsot tartalmaz. Hozzáadtam egy location nevű kulcsot egy üres hash táblával. Ezután hozzáadtam az utolsó két elemet ahhoz a location összefoglaló táblához. Ezt is meg tudjuk csinálni beágyazottan.

$person = @{
    name = 'Kevin'
    age  = 36
    location = @{
        city  = 'Austin'
        state = 'TX'
    }
}

Ez ugyanazt a hash táblát hozza létre, amelyet fent láttunk, és a tulajdonságok ugyanolyan módon érhetők el.

$person.location.city
Austin

Számos módon közelítheti meg az objektumok szerkezetét. Íme egy második módszer a beágyazott hash tábla megtekintésére.

$people = @{
    Kevin = @{
        age  = 36
        city = 'Austin'
    }
    Alex = @{
        age  = 9
        city = 'Austin'
    }
}

Ez ötvözi a hash-táblák használatának a koncepcióját, mint objektumgyűjtemény és mint tulajdonsággyűjtemény. Az értékek továbbra is könnyen elérhetők, még akkor is, ha azokat tetszőleges megközelítéssel ágyazzák be.

PS> $people.kevin.age
36
PS> $people.kevin['city']
Austin
PS> $people['Alex'].age
9
PS> $people['Alex']['City']
Austin

Általában a pont tulajdonságot használom, amikor úgy kezelem, mint egy tulajdonságot. Ezek általában olyan dolgok, amelyeket statikusan definiáltam a kódban, és ismerem őket a fejem tetejéről. Ha végig kell lépnem a listán, vagy programozott módon hozzáférek a kulcsokhoz, a zárójelek segítségével meg kell adnom a kulcs nevét.

foreach($name in $people.Keys)
{
    $person = $people[$name]
    '{0}, age {1}, is in {2}' -f $name, $person.age, $person.city
}

A hash táblák beágyazásának lehetősége nagy rugalmasságot és sok lehetőséget biztosít.

Beágyazott hash-táblák megtekintése

Amint elkezdi a leképezőtáblák beágyazását, szüksége lesz egy egyszerű módszerre, hogy megtekintse azokat a konzolban. Ha az utolsó hashtáblát választom, egy ilyen kimenetet kapok, és csak egy bizonyos mélységig terjed:

PS> $people
Name                           Value
----                           -----
Kevin                          {age, city}
Alex                           {age, city}

Az én kedvenc parancsom ezeknek a dolgoknak a megnézésére a ConvertTo-Json, mert nagyon áttekinthető, és gyakran használom a JSON-t más feladatokhoz.

PS> $people | ConvertTo-Json
{
    "Kevin":  {
                "age":  36,
                "city":  "Austin"
            },
    "Alex":  {
                "age":  9,
                "city":  "Austin"
            }
}

Még ha nem is ismeri a JSON-t, látnia kell, amit keres. Van egy Format-Custom parancs az ilyen strukturált adatokhoz, de még mindig jobban szeretem a JSON nézetet.

Objektumok létrehozása

Néha csak egy objektumra van szüksége, és a tulajdonságok tárolására használt kivonatoló egyszerűen nem végzi el a feladatot. A kulcsokat leggyakrabban oszlopnevekként szeretné látni. Egy pscustomobject megkönnyíti a dolgokat.

$person = [pscustomobject]@{
    name = 'Kevin'
    age  = 36
}

$person

name  age
----  ---
Kevin  36

Még akkor is, ha kezdetben nem pscustomobject hozza létre azt, szükség esetén, amikor kell, később is leadhatja.

$person = @{
    name = 'Kevin'
    age  = 36
}

[pscustomobject]$person

name  age
----  ---
Kevin  36

Már van részletes leírásom a pscustomobject-ről, amit érdemes elolvasnod, miután ezzel végeztél. Sok mindenre épül, amit itt tanultak.

Hash táblák olvasása és írása fájlba

Mentés CSV-be

Az illesztőtábla CSV fájlba mentése az egyik nehézség, amire fentebb utaltam. Konvertálja a hashtáblát egy pscustomobject-ra, és helyesen fogja menteni CSV formátumba. Segít, ha pscustomobject-val kezdi, így megmarad az oszlopsorrend. De szükség esetén egy pscustomobject sorba is helyezheti.

$person | ForEach-Object{ [pscustomobject]$_ } | Export-Csv -Path $path

Nézd meg ismét a beszámolómat egy pscustomobjecthasználatáról.

Beágyazott hashtábla mentése fájlba

Ha egy beágyazott kivonatolót kell mentenem egy fájlba, majd újra beolvasni, akkor a JSON-parancsmagok használatával csinálom.

$people | ConvertTo-Json | Set-Content -Path $path
$people = Get-Content -Path $path -Raw | ConvertFrom-Json

A módszernek két fontos pontja van. Az első probléma az, hogy a JSON több sorban van írva, ezért használnom kell a -Raw opciót, hogy egyetlen sztringként olvashassam vissza. A második az, hogy az importált objektum már nem [hashtable]. Jelenleg ez egy [pscustomobject], amely problémákat okozhat, ha nem számít rá.

Figyeljen oda a mélyen beágyazott hash táblákra. Ha JSON-ra konvertálja, előfordulhat, hogy nem kapja meg a várt eredményeket.

@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json

{
  "a": {
    "b": {
      "c": "System.Collections.Hashtable"
    }
  }
}

A Depth paraméter használatával győződjön meg arról, hogy kibővítette az összes beágyazott hashtáblát.

@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json -Depth 3

{
  "a": {
    "b": {
      "c": {
        "d": "e"
      }
    }
  }
}

Ha importáláskor [hashtable] kell lennie, akkor a Export-CliXml és Import-CliXml parancsokat kell használnia.

JSON átalakítása hashtáblává

Ha JSON-t kell átalakítania egy [hashtable]-vá, van egy módszer, amit ismerek a JavaScriptSerializer használatával .NET-ben.

[Reflection.Assembly]::LoadWithPartialName("System.Web.Script.Serialization")
$JSSerializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
$JSSerializer.Deserialize($json,'Hashtable')

A PowerShell 6-os verziótól kezdve a JSON-támogatás a NewtonSoft JSON.NET használja, és kivonatoló támogatást ad hozzá.

'{ "a": "b" }' | ConvertFrom-Json -AsHashtable

Name      Value
----      -----
a         b

A PowerShell 6.2 hozzáadta a Depth paramétert ConvertFrom-Json. Az alapértelmezett mélység 1024.

Olvasás közvetlenül egy fájlból

Ha egy PowerShell-szintaxist használó kivonatolót tartalmazó fájllal rendelkezik, akkor közvetlenül is importálhatja.

$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create( $content )
$scriptBlock.CheckRestrictedLanguage( $allowedCommands, $allowedVariables, $true )
$hashtable = ( & $scriptBlock )

Importálja a fájl tartalmát egy scriptblock, majd ellenőrzi, hogy nincs-e benne más PowerShell-parancs, mielőtt végrehajtja.

Ha már itt tartunk, tudta, hogy a modulmanifestum (a .psd1 fájl) csak egy hash tábla?

A kulcsok bármilyen objektum lehetnek

A kulcsok legtöbbször csak karakterláncok. Így bármit idézőjelbe tehetünk, és kulcssá tehetjük.

$person = @{
    'full name' = 'Kevin Marquette'
    '#' = 3978
}
$person['full name']

Tehet néhány furcsa dolgot, anélkül, hogy észrevetted volna, hogy meg tudod csinálni.

$person.'full name'

$key = 'full name'
$person.$key

Csak azért, mert tehetsz valamit, még nem jelenti azt, hogy kellene. Ez az utolsó csak úgy néz ki, mint egy hiba, amely arra vár, hogy megtörténjen, és könnyen félreértené bárki, aki elolvassa a kódot.

A kulcsnak gyakorlatilag nem kell karakterláncnak lennie, de könnyebb velük dolgozni, ha csak karakterláncokat használ. Az indexelés azonban nem működik jól az összetett kulcsokkal.

$ht = @{ @(1,2,3) = "a" }
$ht

Name                           Value
----                           -----
{1, 2, 3}                      a

A hashtáblában lévő érték elérése a kulcsa alapján nem mindig működik. Például:

$key = $ht.Keys[0]
$ht.$($key)
a
$ht[$key]
a

Ha a kulcs tömb, a $key változót egy alexpresszióba kell burkolni, hogy taghozzáférés (.) jelöléssel lehessen használni. Tömbindex ([]) jelölést is használhat.

Automatikus változók használata

$PSBoundParameters

$PSBoundParameters egy automatikus változó, amely csak egy függvény környezetében létezik. Az összes paramétert tartalmazza, amellyel a függvényt meghívták. Ez nem pontosan egy hash tábla, de elég közel van ahhoz, hogy úgy kezeld, mintha az lenne.

Ez magában foglalja a kulcsok eltávolítását és más függvényekbe való platformálását. Ha azt tapasztalja, hogy proxyfüggvényeket ír, nézze meg alaposabban ezt az egyet.

Lásd a about_Automatic_Variables további részletekért.

PSBoundParameters buktatója

Fontos megjegyezni, hogy ez csak a paraméterekként átadott értékeket foglalja magában. Ha olyan paraméterekkel is rendelkezik, amelyek alapértelmezett értékeket tartalmaznak, de a hívó nem adja át őket, $PSBoundParameters nem tartalmazzák ezeket az értékeket. Ezt gyakran figyelmen kívül hagyják.

$PSDefaultParameterValues

Ez az automatikus változó lehetővé teszi az alapértelmezett értékek hozzárendelését bármely parancsmaghoz a parancsmag módosítása nélkül. Tekintse meg ezt a példát.

$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"

Ez hozzáad egy bejegyzést a $PSDefaultParameterValues hasítótáblához, amely beállítja a UTF8-et alapértelmezett értékként az Out-File -Encoding paraméter számára. Ez munkamenet-specifikus, ezért el kell helyeznie a $PROFILE-ban.

Ezt gyakran használom olyan értékek előzetes hozzárendeléséhez, amelyeket elég gyakran írok be.

$PSDefaultParameterValues[ "Connect-VIServer:Server" ] = 'VCENTER01.contoso.local'

Ez helyettesítő karaktereket is elfogad, így tömegesen állíthat be értékeket. Az alábbiakban néhány módszert használhat:

$PSDefaultParameterValues[ "Get-*:Verbose" ] = $true
$PSDefaultParameterValues[ "*:Credential" ] = Get-Credential

Részletesebb leírásért nézze meg ezt a nagyszerű cikket a Automatikus Alapértelmezettségekről, amelyet Michael Sorens írt.

Regex $Matches

A -match operátor használatakor a $Matches nevű automatikus változó jön létre az egyezés eredményével. Ha a regexben vannak alkifejezések, ezek az alkifejezések is fel vannak sorolva.

$message = 'My SSN is 123-45-6789.'

$message -match 'My SSN is (.+)\.'
$Matches[0]
$Matches[1]

Névvel ellátott egyezések

Ez az egyik kedvenc funkcióm, amiről a legtöbb ember nem tud. Ha névvel ellátott regex egyezést használ, akkor a találatok között név szerint érheti el az egyezést.

$message = 'My Name is Kevin and my SSN is 123-45-6789.'

if($message -match 'My Name is (?<Name>.+) and my SSN is (?<SSN>.+)\.')
{
    $Matches.Name
    $Matches.SSN
}

A fenti példában a (?<Name>.*) egy elnevezett alkifejezés. Ez az érték ezután a $Matches.Name tulajdonságba kerül.

Group-Object -AsHashtable

A Group-Object egyik kevéssé ismert funkciója, hogy egyes adathalmazokat hash táblává alakíthat az ön számára.

Import-Csv $Path | Group-Object -AsHashtable -Property Email

Ez hozzáadja az egyes sorokat egy hash-táblába, és a megadott tulajdonságot használja kulcsként a hozzáféréshez.

Hashtáblák másolása

Fontos tudni, hogy a hashtábla objektum. És minden változó csak egy objektumra mutató hivatkozás. Ez azt jelenti, hogy több munka szükséges egy hash tábla érvényes másolatának létrehozásához.

Referenciatípusok hozzárendelése

Ha egy kivonatolóval rendelkezik, és egy második változóhoz rendeli hozzá, mindkét változó ugyanarra a kivonatolóra mutat.

PS> $orig = @{name='orig'}
PS> $copy = $orig
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name

Copy: [copy]
Orig: [copy]

Ez kiemeli, hogy ugyanazok, mert az egyikben lévő értékek módosítása a másikban lévő értékeket is módosítja. Ez vonatkozik arra az esetre is, amikor hash táblákat ad át más függvényeknek. Ha ezek a függvények módosítják a hashtáblát, az eredeti is módosul.

Sekély másolatok, egyszintű

Ha van egy egyszerű hash tábla, mint a fentebb említett példánkban, a Clone() használatával készíthetünk egy sekély másolatot.

PS> $orig = @{name='orig'}
PS> $copy = $orig.Clone()
PS> $copy.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.name
PS> 'Orig: [{0}]' -f $orig.name

Copy: [copy]
Orig: [orig]

Ez lehetővé teszi, hogy néhány alapvető módosítást hajtsunk végre az egyiken, amely nem befolyásolja a másikat.

Sekély másolatok, beágyazott

Azért nevezik sekély másolatnak, mert csak az alapszintű tulajdonságokat másolja. Ha az egyik tulajdonság referenciatípus (például egy másik kivonatoló), akkor ezek a beágyazott objektumok továbbra is egymásra mutatnak.

PS> $orig = @{
        person=@{
            name='orig'
        }
    }
PS> $copy = $orig.Clone()
PS> $copy.person.name = 'copy'
PS> 'Copy: [{0}]' -f $copy.person.name
PS> 'Orig: [{0}]' -f $orig.person.name

Copy: [copy]
Orig: [copy]

Láthatja, hogy bár klónoztam a hasítótáblát, a hivatkozás a person-ra nem lett klónozva. Egy mély másolatot kell készítenünk, hogy valóban legyen egy második hasítótábla, amely nem kapcsolódik az elsőhöz.

Mély másolatok

Többféleképpen készíthet mély másolatot egy hash tábláról, és megőrizheti azt hash táblaként. Íme egy függvény, amely a PowerShell használatával hoz létre rekurzív módon egy mély másolatot:

function Get-DeepClone
{
    [CmdletBinding()]
    param(
        $InputObject
    )
    process
    {
        if($InputObject -is [hashtable]) {
            $clone = @{}
            foreach($key in $InputObject.Keys)
            {
                $clone[$key] = Get-DeepClone $InputObject[$key]
            }
            return $clone
        } else {
            return $InputObject
        }
    }
}

Más referenciatípusokat és tömböket nem kezel, de jó kiindulópont.

Egy másik módszer, hogy a .NET segítségével deszerializáljuk azt a CliXml használatával, mint ebben a függvényben:

function Get-DeepClone
{
    param(
        $InputObject
    )
    $TempCliXmlString = [System.Management.Automation.PSSerializer]::Serialize(
        $InputObject, [int32]::MaxValue)
    return [System.Management.Automation.PSSerializer]::Deserialize($TempCliXmlString)
}

A rendkívül nagy hashtáblák esetében a deszerializáló függvény gyorsabb, ahogy kiterjed. Ennek a módszernek a használatakor azonban figyelembe kell venni néhány dolgot. Mivel a CliXml-et használja, memóriaigényes, és ha hatalmas hash táblákat klónoz, az problémát jelenthet. A CliXml egy másik korlátozása a 48-as mélységi korlátozás. Ez azt jelenti, hogy ha egy 48 rétegű beágyazott hash táblából álló hash táblája van, a klónozás sikertelen lesz, és egyáltalán nem jön létre hash tábla.

Bármi egyéb?

Gyorsan nagy utat tettem meg. Remélem, hogy minden alkalommal, amikor olvasod ezt, tanulsz valami újat, vagy jobban megérted. Mivel lefedtem ennek a funkciónak a teljes spektrumát, vannak olyan szempontok, amelyek most nem feltétlenül vonatkoznak Önre. Ez tökéletesen rendben van, és elvárható attól függően, hogy mennyit dolgozik a PowerShell-lel.