Udostępnij za pomocą


Wszystko, co chciałeś wiedzieć o tabelach skrótów

Chcę zrobić krok wstecz i porozmawiać o tabelach skrótów. Używam ich cały czas teraz. Uczyłem kogoś o nich po spotkaniu grupy użytkowników wczoraj wieczorem i zdałem sobie sprawę, że mam ten sam problem ze zrozumieniem ich, co on. Tabele skrótów są naprawdę ważne w programie PowerShell, więc dobrze jest dobrze zrozumieć je.

Uwaga / Notatka

Oryginalna wersja tego artykułu pojawiła się na blogu napisanym przez @KevinMarquette. Zespół programu PowerShell dziękuje Kevinowi za udostępnienie tej zawartości nam. Zapoznaj się ze swoim blogiem na PowerShellExplained.com.

Hashtable jako kolekcja elementów

Chcę, abyś najpierw zobaczył Hashtable jako kolekcję w tradycyjnej definicji hashtable. Ta definicja zapewnia podstawową wiedzę na temat sposobu ich działania, gdy będą używane do bardziej zaawansowanych rzeczy później. Pomijanie tego zrozumienia jest często źródłem nieporozumień.

Co to jest tablica?

Zanim przejdę do tego, czym jest Hashtable, muszę najpierw wspomnieć o tablicach. Na potrzeby tej dyskusji tablica jest listą lub kolekcją wartości lub obiektów.

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

Gdy masz swoje elementy w tablicy, można albo użyć foreach do iteracji listy, albo użyć indeksu, aby uzyskać dostęp do pojedynczych elementów w tablicy.

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

Write-Output $array[3]

Wartości można również aktualizować przy użyciu indeksu w taki sam sposób.

$array[2] = 13

Po prostu porysowałem powierzchnię na tablicach, ale to powinno umieścić je w odpowiednim kontekście, jak przechodzim na tabele skrótów.

Co to jest tabela skrótu?

Zaczynam od podstawowego opisu technicznego tego, czym są tabele skrótów, w ogólnym sensie, zanim przeniosę się do innych sposobów korzystania z nich przez program PowerShell.

Tabela skrótu to struktura danych, podobnie jak tablica, z wyjątkiem przechowywania każdej wartości (obiektu) przy użyciu klucza. To podstawowy magazyn klucz-wartość. Najpierw utworzymy pustą tabelę skrótów.

$ageList = @{}

Zwróć uwagę, że nawiasy klamrowe zamiast nawiasów służą do definiowania tabeli skrótów. Następnie dodajemy element przy użyciu klucza w następujący sposób:

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

$ageList.Add( 'Alex', 9 )

Imię osoby jest kluczem, a jej wiek to wartość, którą chcę zapisać.

Korzystanie z nawiasów do dostępu

Po dodaniu wartości do tabeli skrótów można je wycofać przy użyciu tego samego klucza (zamiast używać indeksu liczbowego, jak w przypadku tablicy).

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

Kiedy chcę poznać wiek Kevina, używam jego imienia, aby go uzyskać. Możemy również użyć tego podejścia, aby dodać lub zaktualizować wartości do tabeli skrótów. Jest to tak samo jak w przypadku użycia powyższej Add() metody.

$ageList = @{}

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

$ageList['Alex'] = 9

Istnieje inna składnia, której można użyć do uzyskiwania dostępu do wartości i aktualizowania ich, które omówię w dalszej sekcji. Jeśli korzystasz z programu PowerShell z innego języka, te przykłady powinny być zgodne z tym, jak można było wcześniej używać tabel skrótów.

Tworzenie tabel skrótów z wartościami

Do tej pory utworzono pustą tabelę skrótów dla tych przykładów. Klucze i wartości można wstępnie wypełnić podczas ich tworzenia.

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

Jako tabela odnośników

Rzeczywista wartość tego typu tabeli skrótu polega na tym, że można ich użyć jako tabeli odnośników. Oto prosty przykład.

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

$server = $environments[$env]

W tym przykładzie określisz środowisko dla zmiennej $env i wybierzesz prawidłowy serwer. Możesz użyć switch($env){...} elementu do wyboru w następujący sposób, ale tabela skrótu jest dobrą opcją.

Staje się to jeszcze lepsze, gdy dynamicznie budujesz tablicę wyszukiwania do późniejszego użycia. Należy więc zastanowić się nad użyciem tego podejścia, jeśli musisz coś porównać. Myślę, że zobaczymy to jeszcze bardziej, gdyby program PowerShell nie był tak dobry podczas filtrowania potoku za pomocą polecenia Where-Object. Jeśli kiedykolwiek jesteś w sytuacji, w której wydajność ma znaczenie, należy rozważyć to podejście.

Nie powiem, że jest to szybsze, ale pasuje do reguły Jeśli wydajność ma znaczenie, przetestuj go.

Wybór wielokrotny

Ogólnie rzecz biorąc, można traktować tabelę skrótu jako parę klucz/wartość, w której podajesz jeden klucz i uzyskujesz jedną wartość. Program PowerShell umożliwia podanie tablicy kluczy w celu uzyskania wielu wartości.

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

W tym przykładzie używam tej samej tablicy mieszającej wspomnianej powyżej i przedstawiam trzy różne style tablicy, aby uzyskać dopasowania. Jest to ukryty klejnot w programie PowerShell, którego większość osób nie zna.

Iterowanie tabel skrótów

Ponieważ tabela skrótów jest kolekcją par klucz/wartość, przechodzisz przez nią w inny sposób niż w przypadku tablicy lub listy elementów.

Pierwszą rzeczą, którą należy zauważyć, jest to, że jeśli zastosujesz operator pipe'a do tablicy mieszającej, potok traktuje ją jak jeden obiekt.

PS> $ageList | Measure-Object
count : 1

Mimo że właściwość Count informuje, ile wartości zawiera.

PS> $ageList.Count
2

Ten problem można obejść, używając Values właściwości , jeśli wszystko, czego potrzebujesz, to tylko wartości.

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

Często bardziej przydatne jest wyliczanie kluczy i używanie ich do uzyskiwania dostępu do wartości.

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

Oto ten sam przykład z pętlą foreach(){...} .

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

Iterujemy każdy klucz w tabeli skrótów, a następnie używamy go, aby uzyskać dostęp do wartości. Jest to typowy wzorzec podczas pracy z tabelami skrótów jako kolekcją.

GetEnumerator()

To prowadzi nas do GetEnumerator() iteracji nad naszą tabelą skrótów.

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

Moduł wyliczający udostępnia jedną parę klucz/wartość po drugiej. Został zaprojektowany specjalnie dla tego przypadku użycia. Dziękuję MarkOwi Krausowi za przypomnienie mi o tym.

Nieprawidłowa Enumeracja

Jednym z ważnych szczegółów jest to, że nie można zmodyfikować tabeli skrótu podczas jej wyliczania. Jeśli zaczniemy od naszego podstawowego przykładu $environments :

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

Próba ustawienia każdego klucza na tę samą wartość serwera kończy się niepowodzeniem.

$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 również zakończy się niepowodzeniem, mimo że wygląda na to, że powinno być również w porządku:

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

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

Sztuczką dla tej sytuacji jest sklonowanie kluczy przed wykonaniem wyliczenia.

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

Uwaga / Notatka

Nie można sklonować tabeli skrótu zawierającej pojedynczy klucz. Program PowerShell zgłasza błąd. Zamiast tego należy przekonwertować właściwość Keys na tablicę, a następnie wykonać iterację na tablicy.

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

Tabela skrótów jako kolekcja właściwości

Do tej pory typy obiektów umieszczanych w naszej tabeli skrótów były takie same. Używałem wieku we wszystkich tych przykładach jako danych, a kluczem było nazwisko osoby. Jest to doskonały sposób, aby przyjrzeć się temu, gdy kolekcja obiektów ma nazwę. Innym typowym sposobem używania tabel skrótów w programie PowerShell jest utrzymywanie kolekcji właściwości, w której klucz jest nazwą właściwości. W tym następnym przykładzie przejdziem do tego pomysłu.

Dostęp oparty na właściwościach

Użycie dostępu opartego na właściwościach zmienia dynamikę tabel skrótów i sposób ich używania w programie PowerShell. Oto nasz zwykły przykład z powyższego traktowania kluczy jako właściwości.

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

Podobnie jak w powyższych przykładach, ten przykład dodaje te klucze, jeśli jeszcze nie istnieją w tabeli skrótów. W zależności od sposobu definiowania kluczy i wartości jest to trochę dziwne lub idealne dopasowanie. Przykład listy wiekowej działał świetnie do tego momentu. Potrzebujemy nowego przykładu, żeby przyszłość była odpowiednia.

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

Ponadto możemy dodawać atrybuty i uzyskiwać dostęp do tych $person atrybutów w następujący sposób.

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

Nagle ta tabela haszująca zaczyna mieć właściwości i działać jak obiekt. Nadal jest to kolekcja rzeczy, więc wszystkie powyższe przykłady nadal mają zastosowanie. Po prostu podchodzimy do niego z innego punktu widzenia.

Sprawdzanie kluczy i wartości

W większości przypadków możesz po prostu przetestować wartość za pomocą następującej metody:

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

To proste, ale było źródłem wielu usterek dla mnie, ponieważ pomijałem jeden ważny szczegół w mojej logice. Zacząłem go używać do testowania, czy klucz był obecny. Gdy wartość była $false lub zero, ta instrukcja zwróci $false nieoczekiwanie.

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

Obejście tego problemu dotyczy wartości zerowych, ale nie rozwiązuje różnicy między $null a nieistniejącymi kluczami. Przez większość czasu nie ma potrzeby wprowadzać tego rozróżnienia, ale istnieją metody na sytuacje, kiedy jest to konieczne.

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

Mamy ContainsValue() na sytuacje, w których należy sprawdzić wartość bez potrzeby znajomości klucza lub przetwarzania całej kolekcji.

Usuwanie i czyszczenie kluczy

Klucze można usunąć za pomocą Remove() metody .

$person.Remove('age')

Przypisanie im $null wartości pozostawia ci klucz, który ma $null wartość.

Typowym sposobem wyczyszczenia tabeli skrótu jest po prostu ustawienie jej jako pustej tabeli skrótu.

$person = @{}

Mimo że to działa, spróbuj użyć Clear() metody .

$person.Clear()

Jest to jedno z tych przypadków, w których użycie metody tworzy samodokumentujący się kod i sprawia, że zamierzenia kodu są bardzo przejrzyste.

Wszystkie zabawne rzeczy

Uporządkowane tabele skrótów

Domyślnie tabele skrótów nie są uporządkowane (ani sortowane). W tradycyjnym kontekście kolejność nie ma znaczenia, gdy zawsze używasz klucza do uzyskiwania dostępu do wartości. Może się okazać, że chcesz, aby właściwości pozostawały w kolejności, w której je definiujesz. Na szczęście istnieje sposób, aby to zrobić za pomocą słowa kluczowego ordered .

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

Teraz, gdy wyliczysz klucze i wartości, pozostają w tej kolejności.

Wbudowane tabele skrótów

Podczas definiowania tabeli skrótu w jednym wierszu można oddzielić pary klucz/wartość średnikiem.

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

Będzie to przydatne, jeśli tworzysz je w potoku.

Wyrażenia niestandardowe w typowych poleceniach dla potoków

Istnieje kilka poleceń cmdlet, które obsługują używanie tabel skrótów do tworzenia niestandardowych lub obliczeniowych właściwości. Ta funkcja jest często widoczna za pomocą instrukcji Select-Object i Format-Table. Tabele skrótów mają specjalną składnię, która wygląda tak po pełnym rozwinięciu.

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

Name to jest to, co polecenie cmdlet nazwałoby tą kolumnę. Jest Expression blokiem skryptu, który jest wykonywany tam, gdzie $_ jest wartością obiektu w potoku. Oto skrypt w akcji:

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

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

Umieściłem to w zmiennej, ale można to łatwo zdefiniować inline i można skrócić Name do n oraz Expression do e podczas tej czynności.

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

Osobiście nie lubię, jak bardzo to wydłuża polecenia i często promuje złe nawyki, których nie będę omawiać. Bardziej prawdopodobne jest, że utworzę nową tabelę skrótu lub pscustomobject zawierające wszystkie żądane pola i właściwości, zamiast korzystać z tego podejścia w skryptach. Ale jest tam dużo kodu, który to robi, więc chciałem, żebyś wiedział o tym. Mówię o tworzeniu pscustomobject później.

Niestandardowe wyrażenie sortowania

Sortowanie kolekcji jest łatwe, jeśli obiekty mają dane, na których chcesz sortować. Możesz dodać dane do obiektu przed posortowaniem lub utworzyć wyrażenie niestandardowe dla elementu Sort-Object.

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

W tym przykładzie pobieram listę użytkowników i używam niestandardowego polecenia cmdlet, aby uzyskać dodatkowe informacje na potrzeby sortowania.

Sortowanie listy tabel skrótów

Jeśli masz listę tabel skrótów, które chcesz sortować, okaże się, że Sort-Object nie traktuje kluczy jako właściwości. Możemy obejść to, używając niestandardowego wyrażenia sortowania.

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

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

Splatting tablic mieszających w poleceniach cmdlet

Jest to jedna z moich ulubionych rzeczy dotyczących hasztablic, których wiele osób nie odkrywa na początku. Chodzi o to, że zamiast dostarczać wszystkie właściwości do polecenia cmdlet w jednym wierszu, można zamiast tego spakować je do tabeli skrótu najpierw. Następnie można nadać funkcji wartość skrótu w specjalny sposób. Oto przykład tworzenia zakresu DHCP w normalny sposób.

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 użycia rozplatania wszystkie te elementy muszą być zdefiniowane w jednym wierszu. Przewija się z ekranu lub zawija się tam, gdziekolwiek ma ochotę. Teraz porównaj to z poleceniem, które używa techniki splattingu.

$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

Użycie znaku @ zamiast $ wywołuje operację splat.

Poświęć chwilę, aby docenić, jak łatwy do przeczytania jest ten przykład. Są one dokładnie tym samym poleceniem z tymi samymi wartościami. Drugi jest łatwiejszy do zrozumienia i utrzymania w przyszłości.

Używam techniki splatting za każdym razem, gdy polecenie jest zbyt długie. Określam "zbyt długo" jako powód, dla którego moje okno się przewija w prawo. Jeśli trafię na trzy właściwości funkcji, jest prawdopodobne, że ją napiszę ponownie, używając rozproszonej tablicy mieszającej.

Przeplatanie dla parametrów opcjonalnych

Jednym z najpopularniejszych sposobów używania splattingu jest radzenie sobie z opcjonalnymi parametrami pochodzącymi z innego miejsca w skrypcie. Załóżmy, że mam funkcję, która opakowuje operację wywołania Get-CimInstance; wywołanie to ma opcjonalny argument $Credential.

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

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

Get-CimInstance @CIMParams

Zaczynam od utworzenia tabeli skrótu z typowymi parametrami. Następnie dodaję element $Credential , jeśli istnieje. Ponieważ używam tutaj splattingu, muszę tylko raz wywołać Get-CimInstance w moim kodzie. Ten wzorzec projektu jest bardzo czysty i może łatwo obsługiwać wiele parametrów opcjonalnych.

Szczerze mówiąc, możesz napisać polecenia, które zezwalają na $null wartości parametrów. Po prostu nie zawsze masz kontrolę nad innymi wywoływanymi poleceniami.

Wiele rozprysków

Możesz splatć wiele tabel skrótów do tego samego polecenia cmdlet. Jeśli ponownie przyjrzymy się naszemu oryginalnemu przykładowi rozpakowywania:

$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

Użyję tej metody, gdy mam wspólny zestaw parametrów, które przekazuję w wielu poleceniach.

Splatting dla czystego kodu

Nie ma nic złego w używaniu rozklepywania (splatting) pojedynczego parametru, jeśli dzięki temu kod staje się bardziej przejrzysty.

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

Przeplatanie plików wykonywalnych

Splatting stosuje się również do niektórych plików wykonywalnych, które używają składni /param:value. Robocopy.exe, na przykład zawiera kilka parametrów, takich jak ten.

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

Nie wiem, że to wszystko jest tak przydatne, ale znalazłem to interesujące.

Dodawanie tabel skrótów

Tabele skrótów obsługują operator dodawania, aby połączyć dwie tabele skrótów.

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

To działa tylko wtedy, gdy dwie tabele skrótów nie dzielą klucza.

Zagnieżdżone tabele skrótów

Możemy używać tabel skrótów jako wartości wewnątrz tabeli skrótów.

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

Zacząłem od podstawowej tabeli skrótowej zawierającej dwa klucze. Dodano klucz o nazwie location z pustą tabelą skrótów. Następnie dodałem dwa ostatnie elementy do tej location tabeli skrótów. Możemy to zrobić również w tekście.

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

To tworzy tę samą tabelę skrótu, którą widzieliśmy powyżej, i umożliwia dostęp do właściwości w ten sam sposób.

$person.location.city
Austin

Istnieje wiele sposobów podejścia do struktury obiektów. Oto drugi sposób na przyjrzenie się zagnieżdżonej tabeli skrótów.

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

Łączy to koncepcję używania tabel skrótów jako kolekcji obiektów i kolekcji właściwości. Wartości są nadal łatwe do uzyskania dostępu nawet wtedy, gdy są zagnieżdżone przy użyciu dowolnego preferowanego podejścia.

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

Mam tendencję do korzystania z właściwości kropki, gdy traktuję go jak nieruchomość. Są to na ogół rzeczy, które zdefiniowałem statycznie w moim kodzie i znam je na pamięć. Jeśli muszę przejść do listy lub programowo uzyskać dostęp do kluczy, użyję nawiasów, aby podać nazwę klucza.

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

Możliwość zagnieżdżania tabel skrótów zapewnia dużą elastyczność i opcje.

Przeglądanie zagnieżdżonych tabel skrótów

Gdy zaczniesz zagnieżdżać tabele skrótów, będziesz potrzebować łatwego sposobu ich przyjrzenia się z konsoli. Jeśli wezmę tę ostatnią tabelę skrótu, otrzymuję dane wyjściowe, które wyglądają tak i sięga tylko do określonej głębokości.

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

Moje polecenie, do którego najczęściej sięgam, aby przyjrzeć się tym rzeczom, to ConvertTo-Json, ponieważ jest bardzo przejrzyste i często używam formatu JSON w innych przypadkach.

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

Nawet jeśli nie znasz formatu JSON, możesz zobaczyć, czego szukasz. Format-Custom Istnieje polecenie do obsługi takich danych strukturalnych, ale i tak wolę widok JSON.

Tworzenie obiektów

Czasami po prostu potrzebujesz mieć obiekt, a użycie tabeli skrótów do przechowywania właściwości po prostu nie spełnia zadania. Najczęściej chcesz zobaczyć klucze jako nazwy kolumn. A pscustomobject czyni to łatwym.

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

$person

name  age
----  ---
Kevin  36

Nawet jeśli początkowo nie utworzysz go jako pscustomobject, zawsze możesz go rzutować później, gdy będzie potrzebne.

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

[pscustomobject]$person

name  age
----  ---
Kevin  36

Mam już szczegółowe opracowanie dotyczące pscustomobject, które warto przeczytać po zakończeniu tego tekstu. Opiera się na wielu rzeczach poznanych tutaj.

Odczytywanie i zapisywanie tabel skrótów do pliku

Zapisywanie w pliku CSV

Zmaganie się z zapisaniem tabeli skrótu do pliku CSV jest jedną z trudności, do których nawiązywałem powyżej. Przekonwertuj tabelę skrótu na element pscustomobject i zapisze ją poprawnie w pliku CSV. Pomoże to, jeśli zaczniesz od wartości pscustomobject , aby kolejność kolumn została zachowana. Można przekształcić ten element na pscustomobject jako wbudowany, jeśli potrzeba.

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

Ponownie zapoznaj się z moim artykułem na temat używania obiektu pscustomobject.

Zapisywanie zagnieżdżonej tabeli skrótu do pliku

Jeśli muszę zapisać zagnieżdżoną tabelę skrótów do pliku, a następnie odczytać ją ponownie, używam cmdlet JSON, aby to zrobić.

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

Istnieją dwa ważne kwestie dotyczące tej metody. Pierwszą rzeczą jest to, że JSON jest zapisywany w formacie wielowierszowym, więc muszę użyć opcji -Raw, aby odczytać go ponownie jako jeden ciąg. Drugim jest to, że zaimportowany obiekt nie jest już obiektem [hashtable]. To teraz [pscustomobject], co może powodować problemy, jeśli się tego nie spodziewasz.

Poszukaj głęboko zagnieżdżonych tabel skrótów. Podczas konwertowania go na format JSON możesz nie uzyskać oczekiwanych wyników.

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

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

Użyj parametru Głębokość , aby upewnić się, że wszystkie zagnieżdżone tabele skrótów zostały rozwinięte.

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

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

Jeśli potrzebujesz, aby był [hashtable] po zaimportowaniu, musisz użyć poleceń Export-CliXml i Import-CliXml.

Konwertowanie formatu JSON na tablicę haszującą

Jeśli musisz przekonwertować JSON na [hashtable], istnieje sposób, o którym wiem, aby to zrobić za pomocą klasy JavaScriptSerializer na platformie .NET.

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

Począwszy od programu PowerShell w wersji 6, obsługa JSON wykorzystuje NewtonSoft JSON.NET i dodaje obsługę hashtable.

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

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

Program PowerShell 6.2 dodał parametr Depth do ConvertFrom-Json polecenia. Domyślna głębokość to 1024.

Odczytywanie bezpośrednio z pliku

Jeśli masz plik zawierający tabelę skrótów przy użyciu składni programu PowerShell, istnieje sposób bezpośredniego zaimportowania go.

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

Importuje zawartość pliku do scriptblock, a następnie sprawdza, czy nie ma żadnych innych poleceń programu PowerShell przed jego wykonaniem.

Czy wiesz, że manifest modułu .psd1 (plik) jest tylko tabelą skrótów?

Klucze mogą być dowolnym obiektem

W większości przypadków klucze są tylko ciągami znaków. Możemy więc umieścić cudzysłowy wokół czegokolwiek i uczynić to kluczem.

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

Możesz zrobić kilka dziwnych rzeczy, których mogłeś nie zdać sobie sprawy, że możesz zrobić.

$person.'full name'

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

Tylko dlatego, że możesz coś zrobić, nie oznacza to, że powinieneś. Ten ostatni wygląda tak, jakby wystąpił błąd i byłby łatwo niezrozumiany przez każdego, kto czyta kod.

Technicznie klucz nie musi być ciągiem, ale łatwiej jest myśleć o tym, jeśli używasz tylko ciągów. Jednak indeksowanie nie działa dobrze z kluczami złożonymi.

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

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

Uzyskiwanie dostępu do wartości w tabeli skrótu przy użyciu klucza nie zawsze działa. Przykład:

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

Gdy klucz jest tablicą, należy owinąć zmienną $key w podwyrażenie, aby można było jej używać z notacją dostępu do składowych (.). Możesz też użyć notacji indeksu tablicy ([]).

Użycie w zmiennych automatycznych

$PSBoundParameters

$PSBoundParameters jest zmienną automatyczną, która istnieje tylko wewnątrz kontekstu funkcji. Zawiera wszystkie parametry wywoływane przez funkcję . To nie jest dokładnie tabela skrótu, ale wystarczająco blisko, że można ją traktować jako taką.

Obejmuje to usuwanie kluczy i odplatanie ich do innych funkcji. Jeśli samodzielnie piszesz funkcje serwera proxy, przyjrzyj się temu bliżej.

Aby uzyskać więcej informacji, zobacz about_Automatic_Variables .

PSBoundParameters pułapka

Należy pamiętać, że obejmuje to tylko wartości, które są przekazywane jako parametry. Jeśli masz również parametry z wartościami domyślnymi, ale nie są przekazywane przez obiekt wywołujący, $PSBoundParameters nie zawiera tych wartości. Jest to często pomijane.

$PSDefaultParameterValues

Ta zmienna automatyczna umożliwia przypisanie wartości domyślnych każdemu poleceniu cmdlet bez zmiany polecenia. Przyjrzyj się temu przykładowi.

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

Spowoduje to dodanie wpisu do tabeli skrótów $PSDefaultParameterValues , która ustawia UTF8 jako wartość domyślną parametru Out-File -Encoding . Jest to specyficzne dla sesji, więc należy umieścić to w $PROFILE.

Często używam tego do wstępnego przypisywania wartości, które często wpisuję.

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

Umożliwia to również akceptowanie symboli wieloznacznych, dzięki czemu można ustawić wartości zbiorczo. Oto kilka sposobów, których można użyć:

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

Aby uzyskać bardziej szczegółowy podział, zobacz ten świetny artykuł na temat Automatycznych Wartości Domyślnych autorstwa Michaela Sorensa.

$Matches wyrażeń regularnych

Gdy używasz operatora -match, jest tworzona zmienna automatyczna $Matches z wynikami dopasowania. Jeśli masz jakiekolwiek wyrażenia podrzędne w regex, te dopasowania podrzędne są również wyświetlane.

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

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

Nazwane dopasowania

Jest to jedna z moich ulubionych funkcji, o których większość ludzi nie wie. Jeśli używasz nazwanego dopasowania wyrażeń regularnych, możesz uzyskać dostęp do tego dopasowania według nazwy w dopasowaniach.

$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
}

W powyższym przykładzie (?<Name>.*) jest nazwanym wyrażeniem podrzędnym. Ta wartość jest następnie umieszczana w właściwości $Matches.Name.

Group-Object -AsHashtable

Jedną z mało znanych funkcji Group-Object jest to, że może przekształcić niektóre zestawy danych w tabelę skrótów.

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

Spowoduje to dodanie każdego wiersza do tabeli skrótu i użycie określonej właściwości jako klucza w celu uzyskania do niego dostępu.

Kopiowanie tabel skrótów

Ważną informacją jest to, że tabele skrótów są obiektami. Każda zmienna jest tylko odwołaniem do obiektu. Oznacza to, że stworzenie prawidłowej kopii tabeli skrótu wymaga więcej pracy.

Przypisywanie typów odwołań

Jeśli masz jedną tabelę skrótów i przypisz ją do drugiej zmiennej, obie zmienne wskazują tę samą tabelę skrótu.

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]

Podkreśla to, że są one takie same, ponieważ zmiana wartości w jednym spowoduje również zmianę wartości w drugiej. Dotyczy to również przekazywania tabel skrótów do innych funkcji. Jeśli te funkcje wprowadzają zmiany w tej tablicy mieszającej, ta oryginalna wersja również zostanie zmieniona.

Płytkie kopie, pojedynczy poziom

Jeśli mamy prostą tabelę skrótu podobną do powyższego przykładu, możemy użyć Clone(), aby utworzyć płytką kopię.

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]

Pozwoli nam to wprowadzić pewne podstawowe zmiany, które nie mają wpływu na drugą.

Płytkie kopie, zagnieżdżone

Powodem, dla którego jest nazywana płytkią kopią, jest to, że kopiuje tylko właściwości na poziomie podstawowym. Jeśli jedna z tych właściwości jest typem odwołania (na przykład inną tabelą skrótów), to zagnieżdżone obiekty nadal będą wzajemnie na siebie wskazywać.

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]

Możesz więc zobaczyć, że mimo sklonowania tabeli skrótu odwołanie do person nie zostało sklonowane. Musimy utworzyć głęboką kopię, aby naprawdę mieć drugą tabelę skrótów, która nie jest połączona z pierwszą.

Kopie głębokie

Istnieje kilka sposobów, aby utworzyć głęboką kopię tabeli skrótu (i zachować ją jako tabelę skrótu). Oto funkcja używająca programu PowerShell do rekursywnego tworzenia kopii głębokiej:

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
        }
    }
}

Nie obsługuje żadnych innych typów odwołań ani tablic, ale jest to dobry punkt wyjścia.

Innym sposobem jest użycie platformy .NET do deserializowania go przy użyciu narzędzia CliXml , jak w tej funkcji:

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

W przypadku bardzo dużych tabel skrótów funkcja deserializacji jest szybsza w miarę skalowania w poziomie. Jednak podczas korzystania z tej metody należy wziąć pod uwagę pewne kwestie. Ponieważ używa on narzędzia CliXml, intensywnie korzysta z pamięci, a jeśli klonujesz ogromne tabele skrótów, może to być problem. Innym ograniczeniem interfejsu CliXml jest ograniczenie głębokości 48. Oznacza to, że jeśli masz tabelę skrótów z 48 warstwami zagnieżdżonych tabel skrótów, klonowanie zakończy się niepowodzeniem i w ogóle nie zostanie wyświetlona tabela skrótu.

Czy coś jeszcze?

Szybko zrobiłem dużo postępów. Mam nadzieję, że za każdym razem, gdy to czytasz, uczysz się czegoś nowego lub lepiej to rozumiesz. Ponieważ omówiłem pełne spektrum tej funkcji, istnieją aspekty, które po prostu mogą nie mieć zastosowania do Ciebie teraz. Jest to całkowicie OK i jest to rodzaj oczekiwanego w zależności od tego, ile pracujesz z programem PowerShell.