Všechno, co jste chtěli vědět o hashtables
Chci udělat krok zpět a mluvit o hashtables. Použiju je celou dobu. Vyučovala jsem někoho o nich po naší schůzce skupiny uživatelů včera večer a uvědomila jsem si, že jsem o nich měla stejné nejasnosti jako on. Hashtables jsou v PowerShellu opravdu důležité, takže je dobré je dobře pochopit.
Poznámka:
Původní verze tohoto článku se objevila na blogu napsané @KevinMarquette. Tým PowerShellu děkujeme Kevinovi za sdílení tohoto obsahu s námi. Prosím, podívejte se na jeho blog na PowerShellExplained.com.
Hashtable as a collection of things
Chci, abyste nejprve viděli hashtable jako kolekci v tradiční definici hashtable. Tato definice vám poskytne základní znalosti o tom, jak fungují, když se později použijí pro pokročilejší věci. Přeskočení tohoto porozumění je často zdrojem nejasností.
Co je pole?
Než se pustím do toho, co je hashovací tabulka , musím nejprve zmínit pole . Pro účely této diskuze je pole seznam nebo kolekce hodnot nebo objektů.
$array = @(1,2,3,5,7,11)
Jakmile budete mít položky v poli, můžete je buď použít foreach
k iteraci seznamu, nebo pomocí indexu získat přístup k jednotlivým prvkům v poli.
foreach($item in $array)
{
Write-Output $item
}
Write-Output $array[3]
Hodnoty můžete také aktualizovat pomocí indexu stejným způsobem.
$array[2] = 13
Právě jsem poškrábal povrch na polích, ale to by je mělo dát do správného kontextu při pohybu na hashtables.
Co je zatřiďovací tabulka?
Začnem základním technickým popisem toho, co jsou hashovací tabulky obecně, než se přesunu na jiné způsoby, jak je PowerShell používá.
Hashtable je datová struktura, podobně jako pole, kromě toho, že každou hodnotu (objekt) ukládáte pomocí klíče. Jedná se o základní úložiště klíč/hodnota. Nejprve vytvoříme prázdnou hashovací tabulku.
$ageList = @{}
Všimněte si, že k definování hashovatelné tabulky se používají složené závorky místo závorek. Pak přidáme položku pomocí klíče, jako je tato:
$key = 'Kevin'
$value = 36
$ageList.add( $key, $value )
$ageList.add( 'Alex', 9 )
Jméno osoby je klíč a jeho věk je hodnota, kterou chci uložit.
Použití závorek pro přístup
Po přidání hodnot do hashovatelné tabulky je můžete pomocí stejného klíče vrátit zpět (místo použití číselného indexu, jaký byste měli pro pole).
$ageList['Kevin']
$ageList['Alex']
Když chci Kevinův věk, používám jeho jméno pro přístup k němu. Tento přístup můžeme použít také k přidání nebo aktualizaci hodnot do hashovatelné tabulky. To se podobá použití výše uvedené add()
funkce.
$ageList = @{}
$key = 'Kevin'
$value = 36
$ageList[$key] = $value
$ageList['Alex'] = 9
Existuje další syntaxe, kterou můžete použít pro přístup k hodnotám a aktualizaci hodnot, které proberem v další části. Pokud přecházíte do PowerShellu z jiného jazyka, měly by se tyto příklady vejít do toho, jak jste možná dříve použili zatřiďovací tabulky.
Vytváření hashovatelných tabulek s hodnotami
Zatím jsem pro tyto příklady vytvořil prázdnou hashovací tabulku. Klíče a hodnoty můžete předem naplnit při jejich vytváření.
$ageList = @{
Kevin = 36
Alex = 9
}
Jako vyhledávací tabulka
Skutečnou hodnotou tohoto typu hashtable je, že je můžete použít jako vyhledávací tabulku. Tady je jednoduchý příklad.
$environments = @{
Prod = 'SrvProd05'
QA = 'SrvQA02'
Dev = 'SrvDev12'
}
$server = $environments[$env]
V tomto příkladu zadáte prostředí pro $env
proměnnou a vyberete správný server. Můžete použít switch($env){...}
pro výběr, jako je tento, ale hashtable je pěkná možnost.
To je ještě lepší, když dynamicky sestavíte vyhledávací tabulku, aby ji později používala. Proto se zamyslete nad použitím tohoto přístupu, když potřebujete něco křížového odkazu. Myslím, že bychom to viděli ještě více, kdyby PowerShell nebyl tak dobrý při filtrování na potrubí s Where-Object
. Pokud jste někdy v situaci, kdy záleží na výkonu, je potřeba tento přístup zvážit.
Neřeknu, že je to rychlejší, ale hodí se do pravidla, jestli je na výkonu důležité, otestujte ho.
Vícenásobný výběr
Obecně si myslíte, že hashtable jako pár klíč/hodnota, kde zadáte jeden klíč a získáte jednu hodnotu. PowerShell umožňuje zadat pole klíčů pro získání více hodnot.
$environments[@('QA','DEV')]
$environments[('QA','DEV')]
$environments['QA','DEV']
V tomto příkladu používám stejnou vyhledávací hashovací tabulku odsud a k získání shody poskytujem tři různé styly pole. Toto je skrytý klenot v PowerShellu, o které většina lidí neví.
Iterace hashtables
Vzhledem k tomu, že hashovatelná tabulka je kolekce párů klíč/hodnota, iterujete ji jinak než u pole nebo normálního seznamu položek.
První věc, kterou si můžete všimnout, je, že pokud předáte hodnotu hashtable, kanál zachází jako s jedním objektem.
PS> $ageList | Measure-Object
count : 1
I když .count
vlastnost udává, kolik hodnot obsahuje.
PS> $ageList.count
2
Tento problém můžete obejít pomocí .values
vlastnosti, pokud potřebujete jenom hodnoty.
PS> $ageList.values | Measure-Object -Average
Count : 2
Average : 22.5
Často je užitečnější vytvořit výčet klíčů a použít je pro přístup k hodnotám.
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
Tady je stejný příklad se smyčkou foreach(){...}
.
foreach($key in $ageList.keys)
{
$message = '{0} is {1} years old' -f $key, $ageList[$key]
Write-Output $message
}
Každý klíč v hashovatelné tabulce procházíme a pak ho použijeme pro přístup k hodnotě. Jedná se o běžný vzor při práci s hashtables jako kolekcí.
GetEnumerator()
To nás přivádí k GetEnumerator()
iteraci přes naši hashovací tabulku.
$ageList.GetEnumerator() | ForEach-Object{
$message = '{0} is {1} years old!' -f $_.key, $_.value
Write-Output $message
}
Enumerátor vám poskytne každý pár klíč/hodnota jeden za druhým. Byl navržen speciálně pro tento případ použití. Děkuji Marku Krausovi, že mi to připomínám.
BadEnumeration
Jedním z důležitých podrobností je, že při vytváření výčtu nemůžete upravit hashovací tabulku. Pokud začneme s naším základním $environments
příkladem:
$environments = @{
Prod = 'SrvProd05'
QA = 'SrvQA02'
Dev = 'SrvDev12'
}
A pokus o nastavení každého klíče na stejnou hodnotu serveru selže.
$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
To také selže, i když to vypadá, že by mělo být také v pořádku:
foreach($key in $environments.keys) {
$environments[$key] = 'SrvDev03'
}
Collection was modified; enumeration operation may not execute.
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException
Trikem této situace je klonování klíčů před provedením výčtu.
$environments.Keys.Clone() | ForEach-Object {
$environments[$_] = 'SrvDev03'
}
Hashtable as a collection of properties
Zatím byl typ objektů, které jsme umístili do naší hashtable, stejný typ objektu. Vevšechch Je to skvělý způsob, jak se na ni podívat, když má vaše kolekce objektů název. Dalším běžným způsobem použití hashtables v PowerShellu je uložení kolekce vlastností, kde klíč je název vlastnosti. V tomto dalším příkladu se k této myšlence připojím.
Přístup na základě vlastností
Použití přístupu na základě vlastností mění dynamiku hashtable a způsob jejich použití v PowerShellu. Tady je náš obvyklý příklad z výše uvedeného zpracování klíčů jako vlastností.
$ageList = @{}
$ageList.Kevin = 35
$ageList.Alex = 9
Stejně jako výše uvedené příklady tento příklad tyto klíče přidá, pokud ještě neexistují v hashtable. V závislosti na tom, jak jste definovali klíče a jaké jsou vaše hodnoty, je to buď trochu podivné, nebo dokonalé přizpůsobení. Příklad věkového seznamu funguje až do tohoto okamžiku skvěle. Potřebujeme nový příklad, abychom se cítili správně.
$person = @{
name = 'Kevin'
age = 36
}
A k tomuto typu můžeme přidat atributy a přistupovat k němu $person
.
$person.city = 'Austin'
$person.state = 'TX'
Najednou se tato hashtable začne cítit a chovat se jako objekt. Stále je to kolekce věcí, takže všechny výše uvedené příklady stále platí. Jen se k tomu přistupujeme z jiného pohledu.
Kontrola klíčů a hodnot
Ve většině případů můžete hodnotu otestovat následujícím způsobem:
if( $person.age ){...}
Je to jednoduché, ale byl zdrojem mnoha chyb pro mě, protože jsem přehlédl jeden důležitý detail v mé logice. Začal jsem ho používat k otestování, jestli byl klíč přítomen. Pokud hodnota byla $false
nebo nula, tento příkaz by se neočekávaně vrátil $false
.
if( $person.age -ne $null ){...}
Tento problém se dá obejít u nulových hodnot, ale ne pro $null vs. neexistující klíče. Ve většině případů nemusíte toto rozlišení rozlišovat, ale existují funkce pro to, kdy to uděláte.
if( $person.ContainsKey('age') ){...}
Máme ContainsValue()
také pro situaci, kdy potřebujete otestovat hodnotu, aniž byste znali klíč nebo iterovali celou kolekci.
Odebrání a vymazání klíčů
Pomocí funkce můžete klíče odebrat .Remove()
.
$person.remove('age')
Když jim $null
přiřadíte hodnotu, zůstanete jen klíčem, který má $null
hodnotu.
Běžným způsobem, jak vymazat hashovací tabulku, je jednoduše ji inicializovat na prázdnou hashtable.
$person = @{}
I když to funguje, zkuste místo toho použít clear()
funkci.
$person.clear()
Jedná se o jednu z těchto instancí, kdy použití funkce vytvoří kód pro samodokumentování a záměry kódu jsou velmi čisté.
Všechny zábavné věci
Seřazené hashovací tabulky
Ve výchozím nastavení nejsou hashtable seřazené (ani seřazené). V tradičním kontextu nezáleží na pořadí, když pro přístup k hodnotám vždy používáte klíč. Můžete zjistit, že chcete, aby vlastnosti zůstaly v pořadí, v jakém je definujete. Naštěstí existuje způsob, jak to udělat pomocí klíčového ordered
slova.
$person = [ordered]@{
name = 'Kevin'
age = 36
}
Když teď vytvoříte výčet klíčů a hodnot, zůstanou v daném pořadí.
Vložené hashovací tabulky
Při definování hashovací tabulky na jednom řádku můžete páry klíč/hodnota oddělit středníkem.
$person = @{ name = 'kevin'; age = 36; }
To přijde vhod, pokud je vytváříte na potrubí.
Vlastní výrazy v běžných příkazech kanálu
Existuje několik rutin, které podporují použití hashtables k vytvoření vlastních nebo počítaných vlastností. Běžně se to s a Select-Object
Format-Table
. Hashtables mají speciální syntaxi, která vypadá takto při úplném rozbalení.
$property = @{
name = 'totalSpaceGB'
expression = { ($_.used + $_.free) / 1GB }
}
To name
je to, co by rutina označovala daný sloupec. Jedná se expression
o blok skriptu, který se spustí, kde $_
je hodnota objektu na kanálu. Tady je skript v akci:
$drives = Get-PSDrive | Where Used
$drives | Select-Object -Property name, $property
Name totalSpaceGB
---- ------------
C 238.472652435303
Umístila jsem to do proměnné, ale mohla by být snadno definována vložená a můžete ji zkrátit name
na n
a expression
na e
dobu, kdy jste na ní.
$drives | Select-Object -property name, @{n='totalSpaceGB';e={($_.used + $_.free) / 1GB}}
Osobně se mi nelíbí, jak dlouho to dělá příkazy a často podporuje některé špatné chování, ke kterým se nedostanem. Pravděpodobněji vytvořím novou hashovatelnou tabulku nebo pscustomobject
se všemi poli a vlastnostmi, které chci místo použití tohoto přístupu ve skriptech. Ale tam je hodně kódu, co to dělá, takže jsem chtěl, abys o tom věděl. Mluvím o vytvoření pscustomobject
později.
Vlastní výraz řazení
Kolekci můžete snadno seřadit, pokud mají objekty data, podle kterých chcete data seřadit. Data můžete buď přidat do objektu před řazením, nebo vytvořit vlastní výraz pro Sort-Object
.
Get-ADUser | Sort-Object -Property @{ e={ Get-TotalSales $_.Name } }
V tomto příkladu vezmu seznam uživatelů a pomocí nějaké vlastní rutiny získám další informace jen pro řazení.
Seřazení seznamu hashovatelných tabulek
Pokud máte seznam hashovacích tabulek, které chcete seřadit, zjistíte, že Sort-Object
klíče nejsou považovány za vlastnosti. Toto zaokrouhlování můžeme získat pomocí vlastního výrazu řazení.
$data = @(
@{name='a'}
@{name='c'}
@{name='e'}
@{name='f'}
@{name='d'}
@{name='b'}
)
$data | Sort-Object -Property @{e={$_.name}}
Splatting hashtables at cmdlets
Toto je jedna z mých oblíbených věcí o hashtables, které mnoho lidí nezjišťuje brzy. Myšlenka spočívá v tom, že místo poskytnutí všech vlastností rutině na jednom řádku je můžete nejprve zabalit do hashovatelné tabulky. Pak můžete funkci hashovat speciálním způsobem. Tady je příklad vytvoření oboru DHCP normálním způsobem.
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"
Bez použití splattingu musí být všechny tyto věci definovány na jednom řádku. Buď se posune mimo obrazovku, nebo se zabalí tam, kde to někdy vypadá. Teď to porovnejte s příkazem, který používá splatting.
$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
Použití @
znaménka místo $
toho, co vyvolá operaci splat.
Jen chvilku si vážíme toho, jak snadno se tento příklad čte. Jedná se o úplně stejný příkaz se všemi stejnými hodnotami. Druhá je jednodušší pochopit a udržovat se v budoucnu.
Používám splatting, kdykoli se příkaz dostane příliš dlouho. Definujem příliš dlouho, protože okno se posunuje doprava. Pokud pro funkci narazím na tři vlastnosti, je možné, že ji přepíšem pomocí splattované hashtable.
Splatting for optional parameters
Jedním z nejběžnějších způsobů použití splattingu je zpracování volitelných parametrů, které pocházejí z nějakého jiného místa ve skriptu. Řekněme, že mám funkci, která zabalí Get-CIMInstance
volání, které má volitelný $Credential
argument.
$CIMParams = @{
ClassName = 'Win32_Bios'
ComputerName = $ComputerName
}
if($Credential)
{
$CIMParams.Credential = $Credential
}
Get-CIMInstance @CIMParams
Začnem vytvořením hashovatelného objektu s běžnými parametry. Pak přidám, $Credential
jestli existuje.
Protože tady používám splatting, potřebuji jen jednou volání Get-CIMInstance
v kódu. Tento vzor návrhu je velmi čistý a dokáže snadno zpracovat spoustu volitelných parametrů.
Abyste byli spravedliví, mohli byste napsat příkazy, které umožňují $null
hodnoty parametrů. Prostě nemáte vždy kontrolu nad ostatními příkazy, které voláte.
Víceplatek
Do stejné rutiny můžete splatovat několik hashovatelných tabulek. Pokud se k našemu původnímu příkladu splattingu znovu podíváme:
$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
Tuto metodu použijem, když mám společnou sadu parametrů, které předávám spoustě příkazů.
Splatting for clean code
Pokud kód zčistíte, není nic špatného při dělení jednoho parametru.
$log = @{Path = '.\logfile.log'}
Add-Content "logging this command" @log
Splatting spustitelných souborů
Splatting funguje také u některých spustitelných souborů, které používají /param:value
syntaxi. Robocopy.exe
Například má některé parametry, jako je tento.
$robo = @{R=1;W=1;MT=8}
robocopy source destination @robo
Nevím, že to je všechno, co je to užitečné, ale našel jsem to zajímavé.
Přidání hashtables
Hashtables podporují operátor sčítání pro kombinování dvou hashtables.
$person += @{Zip = '78701'}
To funguje jenom v případě, že tyto dvě hashovací tabulky nesdílí klíč.
Vnořené hashovatelné tabulky
Hashtables můžeme použít jako hodnoty uvnitř hashtable.
$person = @{
name = 'Kevin'
age = 36
}
$person.location = @{}
$person.location.city = 'Austin'
$person.location.state = 'TX'
Začal jsem se základní hashtable obsahující dva klíče. Přidali jsem klíč volaný location
s prázdnou hashtable. Pak jsem do této location
hashtable přidal poslední dvě položky. Můžeme to udělat i v textu.
$person = @{
name = 'Kevin'
age = 36
location = @{
city = 'Austin'
state = 'TX'
}
}
Tím se vytvoří stejná hashovatelná tabulka, kterou jsme viděli výše, a stejným způsobem má přístup k vlastnostem.
$person.location.city
Austin
Existuje mnoho způsobů, jak přistupovat ke struktuře objektů. Tady je druhý způsob, jak se podívat na vnořenou hashovací tabulku.
$people = @{
Kevin = @{
age = 36
city = 'Austin'
}
Alex = @{
age = 9
city = 'Austin'
}
}
To kombinuje koncept použití hashtable jako kolekce objektů a kolekce vlastností. Hodnoty jsou stále snadno přístupné, i když jsou vnořené pomocí libovolného přístupu, který dáváte přednost.
PS> $people.kevin.age
36
PS> $people.kevin['city']
Austin
PS> $people['Alex'].age
9
PS> $people['Alex']['City']
Austin
Mám tendenci používat tečku vlastnost, když s ní zacházím jako s nemovitostí. To jsou obecně věci, které jsem v kódu definoval staticky a já je znám v horní části hlavy. Pokud potřebuji procházet seznam nebo programově přistupovat ke klíčům, použijte hranaté závorky k zadání názvu klíče.
foreach($name in $people.keys)
{
$person = $people[$name]
'{0}, age {1}, is in {2}' -f $name, $person.age, $person.city
}
Možnost vnořit hashtables vám dává spoustu flexibility a možností.
Zobrazení vnořených hashtables
Jakmile začnete vnořovat hashovací tabulky, budete potřebovat snadný způsob, jak se na ně podívat z konzoly. Pokud si vezmu poslední hashovací tabulku, získám výstup, který vypadá takto, a to jde jenom do hloubky:
PS> $people
Name Value
---- -----
Kevin {age, city}
Alex {age, city}
Přejděte na příkaz, abyste se podívali na tyto věci, je ConvertTo-JSON
to proto, že je to velmi čisté a často používám JSON na jiné věci.
PS> $people | ConvertTo-Json
{
"Kevin": {
"age": 36,
"city": "Austin"
},
"Alex": {
"age": 9,
"city": "Austin"
}
}
I když json neznáte, měli byste být schopni zjistit, co hledáte. Format-Custom
Existuje příkaz pro strukturovaná data, jako je tato, ale stále se mi líbí zobrazení JSON lépe.
Vytváření objektů
Někdy stačí mít objekt a pomocí hashtable k uložení vlastností se úloha nedokončí. Nejčastěji se mají klíče zobrazovat jako názvy sloupců. To pscustomobject
je snadné.
$person = [pscustomobject]@{
name = 'Kevin'
age = 36
}
$person
name age
---- ---
Kevin 36
I když ho pscustomobject
zpočátku nevytváříte, můžete ho kdykoli později v případě potřeby přetypovat.
$person = @{
name = 'Kevin'
age = 36
}
[pscustomobject]$person
name age
---- ---
Kevin 36
Už mám podrobný zápis pro pscustomobject , že byste měli jít číst po tomto. Vychází z mnoha věcí, které se zde naučily.
Čtení a zápis hashovatelných tabulek do souboru
Ukládání do sdíleného svazku clusteru
Problém s získáním hashovatelné tabulky pro uložení do sdíleného svazku clusteru je jedním z potíží, na které jsem odkazoval výše. Převeďte zatřiďovací tabulku na hodnotu a pscustomobject
uloží se správně do souboru CSV. Pomůže vám to, když začnete tak pscustomobject
, aby se zachovalo pořadí sloupců. V případě potřeby ho ale můžete přetypovat na vloženou pscustomobject
.
$person | ForEach-Object{ [pscustomobject]$_ } | Export-CSV -Path $path
Znovu se podívejte na můj zápis pomocí pscustomobject.
Uložení vnořené hashovací tabulky do souboru
Pokud potřebuji uložit vnořenou hashovací tabulku do souboru a pak ji znovu přečíst, použijem k tomu rutiny JSON.
$people | ConvertTo-JSON | Set-Content -Path $path
$people = Get-Content -Path $path -Raw | ConvertFrom-JSON
Tato metoda obsahuje dva důležité body. Nejprve je json napsaný ve víceřádkovém formátu, takže potřebuji použít -Raw
možnost pro jeho čtení zpět do jednoho řetězce. Druhý je, že importovaný objekt již [hashtable]
není . Je to teď [pscustomobject]
a to může způsobit problémy, pokud to neočekáváte.
Sledujte hluboko vnořené hashovací tabulky. Když ho převedete na JSON, nemusí se zobrazit očekávané výsledky.
@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json
{
"a": {
"b": {
"c": "System.Collections.Hashtable"
}
}
}
Pomocí parametru Depth se ujistěte, že jste rozšířili všechny vnořené hashtables.
@{ a = @{ b = @{ c = @{ d = "e" }}}} | ConvertTo-Json -Depth 3
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Pokud potřebujete, aby byl při importu [hashtable]
, musíte použít příkazy Export-CliXml
a Import-CliXml
příkazy.
Převod JSON na hashtable
Pokud potřebujete převést JSON na , [hashtable]
existuje jeden způsob, jak to vím udělat s JavaScriptSerializer v .NET.
[Reflection.Assembly]::LoadWithPartialName("System.Web.Script.Serialization")
$JSSerializer = [System.Web.Script.Serialization.JavaScriptSerializer]::new()
$JSSerializer.Deserialize($json,'Hashtable')
Počínaje PowerShellem v6 používá podpora JSON newtonsoft JSON.NET a přidává podporu hashtable.
'{ "a": "b" }' | ConvertFrom-Json -AsHashtable
Name Value
---- -----
a b
PowerShell 6.2 přidal parametr Hloubka do ConvertFrom-Json
souboru . Výchozí hloubka je 1024.
Čtení přímo ze souboru
Pokud máte soubor, který obsahuje hashtable pomocí syntaxe PowerShellu, existuje způsob, jak ho přímo importovat.
$content = Get-Content -Path $Path -Raw -ErrorAction Stop
$scriptBlock = [scriptblock]::Create( $content )
$scriptBlock.CheckRestrictedLanguage( $allowedCommands, $allowedVariables, $true )
$hashtable = ( & $scriptBlock )
Naimportuje obsah souboru do scriptblock
souboru a pak zkontroluje, jestli neobsahuje žádné další příkazy PowerShellu, než ho spustí.
Na této poznámce jste věděli, že manifest modulu (soubor PSD1) je jen hashtable?
Klíče můžou být libovolný objekt.
Ve většině případů jsou klíče jen řetězce. Takže můžeme dát uvozovky kolem čehokoli a udělat to klíčem.
$person = @{
'full name' = 'Kevin Marquette'
'#' = 3978
}
$person['full name']
Můžete udělat nějaké divné věci, které jste si možná neuvědomili, že byste mohli dělat.
$person.'full name'
$key = 'full name'
$person.$key
Jen proto, že můžete něco udělat, neznamená to, že byste měli. Poslední vypadá jako chyba, která čeká na to, že se stane a bude snadno pochopena tím, že někdo čte váš kód.
Technicky vzato váš klíč nemusí být řetězec, ale je jednodušší uvažovat, pokud používáte jenom řetězce. Indexování ale nefunguje s komplexními klíči dobře.
$ht = @{ @(1,2,3) = "a" }
$ht
Name Value
---- -----
{1, 2, 3} a
Přístup k hodnotě v hashovatelné tabulce pomocí jeho klíče nefunguje vždy. Příklad:
$key = $ht.keys[0]
$ht.$($key)
a
$ht[$key]
a
Pokud je klíčem pole, musíte proměnnou zabalit $key
do dílčího výrazu, aby ji bylo možné použít s zápisem přístupu člena (.
). Nebo můžete použít notaci indexu pole ([]
).
Použití v automatických proměnných
$PSBoundParameters
$PSBoundParameters je automatická proměnná, která existuje pouze v kontextu funkce. Obsahuje všechny parametry, se kterými byla funkce volána. Není to přesně hashovatelná tabulka, ale dostatečně blízko, abyste ji mohli považovat za takovou.
To zahrnuje odebrání klíčů a jejich dělení na jiné funkce. Pokud zjistíte, že píšete proxy funkce, podívejte se na tuto funkci podrobněji.
Další podrobnosti najdete v about_Automatic_Variables .
PSBoundParameters gotcha
Je důležité si uvědomit, že to zahrnuje pouze hodnoty, které se předávají jako parametry. Pokud máte také parametry s výchozími hodnotami, ale volající je nepředává, $PSBoundParameters
neobsahuje tyto hodnoty. To se běžně přehlíží.
$PSDefaultParameterValues
Tato automatická proměnná umožňuje přiřadit výchozí hodnoty jakékoli rutině beze změny rutiny. Podívejte se na tento příklad.
$PSDefaultParameterValues["Out-File:Encoding"] = "UTF8"
Tím se přidá položka do $PSDefaultParameterValues
hashtable, která se nastaví UTF8
jako výchozí hodnota parametru Out-File -Encoding
. Toto je specifické pro relaci, takže byste ho měli umístit do svého $profile
.
Používám to často k předběžnému přiřazení hodnot, které zadám poměrně často.
$PSDefaultParameterValues[ "Connect-VIServer:Server" ] = 'VCENTER01.contoso.local'
To také přijímá zástupné cardy, abyste mohli hodnoty nastavit hromadně. Tady je několik způsobů, jak ho můžete použít:
$PSDefaultParameterValues[ "Get-*:Verbose" ] = $true
$PSDefaultParameterValues[ "*:Credential" ] = Get-Credential
Podrobnější rozpis najdete v tomto skvělém článku o automatických výchozích nastaveních od Michaela Sorense.
$Matches regulárních výrazů
Při použití operátoru -match
se vytvoří automatická proměnná $matches
s výsledky shody. Pokud máte v regulárním výrazu nějaké dílčí výrazy, zobrazí se také tyto dílčí shody.
$message = 'My SSN is 123-45-6789.'
$message -match 'My SSN is (.+)\.'
$Matches[0]
$Matches[1]
Pojmenované shody
Toto je jedna z mých oblíbených funkcí, o kterých většina lidí neví. Pokud použijete pojmenovanou shodu regulárních výrazů, můžete k této shodě přistupovat podle názvu.
$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
}
V předchozím příkladu (?<Name>.*)
je pojmenovaný dílčí výraz. Tato hodnota se pak umístí do $Matches.Name
vlastnosti.
Group-Object -AsHashtable
Jednou z málo známých funkcí Group-Object
je, že může některé datové sady převést na zatřiďovací tabulku.
Import-CSV $Path | Group-Object -AsHashtable -Property email
Tím se každý řádek přidá do hashtable a použije zadanou vlastnost jako klíč pro přístup k němu.
Kopírování hashovatelných tabulek
Jednou z důležitých věcí, kterou je potřeba vědět, je, že hashovací tabulky jsou objekty. Každá proměnná je jen odkazem na objekt. To znamená, že vytvoření platné kopie hashovatelné tabulky trvá více práce.
Přiřazení typů odkazů
Pokud máte jednu hashovací tabulku a přiřadíte ji druhé proměnné, obě proměnné odkazují na stejnou hashovací tabulku.
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]
To zvýrazňuje, že jsou stejné, protože změna hodnot v jednom z nich také změní hodnoty v druhé. To platí také při předávání hashtables do jiných funkcí. Pokud tyto funkce změní tuto hashovací tabulku, původní funkce se také změní.
Mělké kopie, jedna úroveň
Pokud máme jednoduchou hashovací tabulku, jako je náš příklad výše, můžeme použít .Clone()
k vytvoření mělké kopie.
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]
To nám umožní provést některé základní změny, které nemají vliv na druhý.
Mělké kopie, vnořené
Důvodem, proč se nazývá mělká kopie, je, že kopíruje pouze vlastnosti základní úrovně. Pokud je jednou z těchto vlastností referenční typ (například jiná hashtable), budou tyto vnořené objekty stále odkazovat na sebe.
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]
Takže vidíte, že i když jsem naklonoval hashtable, odkaz nebyl person
naklonován. Potřebujeme vytvořit hloubkovou kopii, abychom skutečně měli druhou hashovací tabulku, která není propojená s prvním.
Hloubkové kopie
Existuje několik způsobů, jak vytvořit hloubkovou kopii hashovatelné tabulky (a zachovat ji jako hashtable). Tady je funkce využívající PowerShell k rekurzivnímu vytvoření hloubkové kopie:
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
}
}
}
Nezpracuje žádné jiné odkazové typy nebo pole, ale je to dobrý výchozí bod.
Dalším způsobem je použití .Net k deserializaci pomocí cliXml , jako je tato funkce:
function Get-DeepClone
{
param(
$InputObject
)
$TempCliXmlString = [System.Management.Automation.PSSerializer]::Serialize($obj, [int32]::MaxValue)
return [System.Management.Automation.PSSerializer]::Deserialize($TempCliXmlString)
}
U extrémně velkých hashovatelných tabulek je funkce deserializace rychlejší při horizontálním navýšení kapacity. Při použití této metody je však potřeba zvážit několik věcí. Vzhledem k tomu, že používá CliXml, je náročné na paměť a pokud klonujete obrovské hashtables, může to být problém. Dalším omezením cliXml je omezení hloubky 48. To znamená, že pokud máte hashtable s 48 vrstvami vnořených hashtable, klonování selže a nebude vůbec výstupem žádná hashovatelná tabulka.
Cokoli jiného?
Rychle jsem probral spoustu země. Doufám, že odcházíš o něco nového, nebo pochopíš to lépe pokaždé, když si to přečteš. Vzhledem k tomu, že jsem probral celé spektrum této funkce, existují aspekty, které se na vás právě teď nemusí vztahovat. To je naprosto v pořádku a podle toho, jak moc pracujete s PowerShellem, se očekává.