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 ostatniej nocy i zdałem sobie sprawę, że mam takie samo zamieszanie o nich, jak miał. Tabele skrótów są naprawdę ważne w programie PowerShell, więc dobrze jest dobrze zrozumieć je.

Uwaga

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.

Tabela skrótów jako kolekcja rzeczy

Chcę, aby najpierw zobaczyć tabelę skrótu jako kolekcję w tradycyjnej definicji tabeli skrótów. 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 wskoczę do tego, co 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)

Po utworzeniu elementów w tablicy można wykonać foreach iterację na liście lub użyć indeksu, aby uzyskać dostęp do poszczególnych 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. Jest 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ę i nazwisko osoby jest kluczem, a ich wiek to wartość, którą chcę zapisać.

Używanie nawiasów 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ę, aby wiek Kevina, używam jego imienia, aby uzyskać do niego dostęp. 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 użycie powyższej add() funkcji.

$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 kompilujesz tabelę odnośników 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.

Multiselection

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 tabeli skrótu odnośnika z powyższego i udostępniam 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ótu jest kolekcją par klucz/wartość, iterujesz ją inaczej niż w przypadku tablicy lub zwykłej listy elementów.

Pierwszą rzeczą, którą należy zauważyć, jest to, że w przypadku potoku tablicy skrótów potok traktuje go jak jeden obiekt.

PS> $ageList | Measure-Object
count : 1

Mimo że właściwość informuje o tylu .count wartościach, które 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
}

Przechodzimy przez każdy klucz w tabeli skrótów, a następnie używamy go do uzyskiwania dostępu 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.

BadEnumeration

Jednym z ważnych szczegółów jest to, że nie można zmodyfikować tabeli skrótu podczas jego 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'
}

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

Do tej pory typ obiektów umieszczonych w naszej tabeli skrótu był tym samym typem obiektu. Używałem wieku we wszystkich tych przykładach, a klucz był nazwiskiem 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, aby czuć się w przyszłości.

$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 skrótu zaczyna czuć się 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 ){...}

Dotyczy to problemu z zerową wartością, ale nie $null a nie istnieją klucze. Przez większość czasu nie trzeba wprowadzać tego rozróżnienia, ale istnieją funkcje, które należy wykonać.

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

Mamy ContainsValue() również sytuację, w której należy przetestować wartość bez znajomości klucza lub iterowania całej kolekcji.

Usuwanie i czyszczenie kluczy

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

$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 zainicjowanie go do pustej tabeli skrótu.

$person = @{}

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

$person.clear()

Jest to jedno z tych wystąpień, w których użycie funkcji tworzy kod samodzielnie dokumentujący i sprawia, że intencje kodu są bardzo czyste.

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 potoku

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

Polecenie name cmdlet będzie oznaczać kolumnę. Jest expression to blok skryptu, który jest wykonywany, 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 ją łatwo zdefiniować w tekście i można skrócić name do n i expression do e czasu, gdy jesteś na nim.

$drives | Select-Object -property name, @{n='totalSpaceGB';e={($_.used + $_.free) / 1GB}}

Osobiście nie lubię, jak długo to sprawia, że polecenia i często promuje niektóre złe zachowania, że nie dostaję się. Bardziej prawdopodobne jest utworzenie nowej tabeli skrótu lub pscustomobject wszystkich pól i właściwości, które chcę zamiast używać 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 tylko dla 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 klucze nie są traktowane jako właściwości. Możemy uzyskać rundę, która jest używana przy użyciu niestandardowego wyrażenia sortowania.

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

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

Splatting hashtables at cmdlets (Splatting hashtables at cmdlets(Splatting hashtables at cmdlets

Jest to jedna z moich ulubionych rzeczy na temat skrótów, 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 owija się tam, gdzie kiedykolwiek wygląda. Teraz porównaj to z poleceniem, które używa wplatania.

$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 $ znaku wywołuje operację splat.

Po prostu pośmiń chwilę, aby docenić, jak łatwy jest przeczytanie tego przykładu. 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 splatting za każdym razem, gdy polecenie staje się zbyt długie. Zdefiniuję zbyt długo, ponieważ powoduje przewinięcie okna w prawo. Jeśli trafię trzy właściwości dla funkcji, kursy są, że napiszę go ponownie przy użyciu splatted hashtable.

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 Get-CIMInstance wywołanie, które ma opcjonalny $Credential argument.

$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 wplatania, muszę tylko raz wywołać Get-CIMInstance metodę w kodzie. Ten wzorzec projektu jest bardzo czysty i może łatwo obsługiwać wiele parametrów opcjonalnych.

Aby być uczciwym, możesz napisać polecenia, aby zezwolić na $null wartości parametrów. Po prostu nie zawsze masz kontrolę nad innymi wywoływanymi poleceniami.

Wiele płyt

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

$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 przekazywanych do wielu poleceń.

Rozplatanie dla czystego kodu

Nie ma nic złego w przypadku przeplatania pojedynczego parametru, jeśli kod jest czyszczący.

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

Przeplatanie plików wykonywalnych

Funkcja Splatting działa również na niektórych plikach wykonywalnych, które używają /param:value składni. 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'}

Działa to tylko wtedy, gdy dwie tabele skrótów nie współużytkują klucza.

Zagnieżdżone tabele skrótów

Możemy użyć 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'
    }
}

Spowoduje to utworzenie tej samej tabeli skrótu, którą widzieliśmy powyżej i może uzyskać dostęp do właściwości w taki 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 od góry głowy. 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 biorę tę ostatnią tabelę skrótu, otrzymuję dane wyjściowe, które wyglądają następująco i tylko idzie tak głęboko:

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

Moje polecenie przejdź do polecenia, aby przyjrzeć się tym rzeczom, ponieważ ConvertTo-JSON jest bardzo czyste i często używam kodu JSON w innych rzeczach.

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 dla danych strukturalnych w następujący sposób, ale nadal lubię widok JSON lepiej.

Tworzenie obiektów

Czasami wystarczy mieć obiekt i użycie tabeli skrótu do przechowywania właściwości po prostu nie jest wykonywane zadanie. Najczęściej chcesz zobaczyć klucze jako nazwy kolumn. A pscustomobject sprawia, że łatwe.

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

$person

name  age
----  ---
Kevin  36

Nawet jeśli nie utworzysz go jako pscustomobject początkowo, zawsze możesz go rzutować później w razie potrzeby.

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

[pscustomobject]$person

name  age
----  ---
Kevin  36

Mam już szczegółowe zapisy dla pscustomobject , które należy przeczytać po tym. Opiera się na wielu rzeczach poznanych tutaj.

Odczytywanie i zapisywanie tabel skrótów do pliku

Zapisywanie w pliku CSV

Zmaganie się z uzyskaniem tabeli skrótu, aby zapisać w woluminie CSV jest jednym z trudności, które odnosiłem się do powyższych. 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. Ale w razie potrzeby można go rzutować do wbudowanego pscustomobject .

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

Ponownie zapoznaj się z moim zapisem przy użyciu obiektu pscustomobject.

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

Jeśli muszę zapisać zagnieżdżona tabelę skrótów do pliku, a następnie odczytać go ponownie, użyję poleceń 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. Najpierw jest to, że kod JSON jest zapisywany wielowierszowy, więc muszę użyć -Raw opcji , aby odczytać go z powrotem do jednego ciągu. Drugim jest to, że zaimportowany obiekt nie jest już obiektem [hashtable]. Jest to teraz [pscustomobject] element i, który może powodować problemy, jeśli 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 go do [hashtable] zaimportowania, musisz użyć Export-CliXml poleceń i Import-CliXml .

Konwertowanie formatu JSON na tabelę skrótów

Jeśli musisz przekonwertować kod JSON na [hashtable]element , istnieje jeden ze sposobów, o których wiem, aby to zrobić za pomocą interfejsu 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 formatu JSON używa JSON.NET NewtonSoft i dodaje obsługę tabeli skrótu.

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

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

Program PowerShell 6.2 dodał parametr Depth do ConvertFrom-Jsonpolecenia . 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 scriptblockpliku , a następnie sprawdza, aby upewnić się, że nie ma żadnych innych poleceń programu PowerShell przed jego wykonaniem.

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

Klucze mogą być dowolnym obiektem

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

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

Możesz zrobić kilka dziwnych rzeczy, których możesz nie zrealizować.

$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. Na przykład:

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

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

Używanie 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 go traktować jak jeden.

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 gotcha

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 do dowolnego polecenia cmdlet bez zmiany polecenia cmdlet. 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, dlatego należy umieścić ją w pliku $profile.

Używam tego często do wstępnego przypisywania wartości, które typuję dość często.

$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 michaela Sorensa.

$Matches wyrażeń regularnych

Gdy używasz -match operatora, jest tworzona zmienna automatyczna o nazwie $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 (?<Name>.*) przykładzie element jest nazwanym wyrażeniem podrzędnym. Ta wartość jest następnie umieszczana we $Matches.Name właściwości .

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

Jedną z ważnych rzeczy jest to, że tabele skrótów są obiektami. Każda zmienna jest tylko odwołaniem do obiektu. Oznacza to, że potrzeba więcej pracy, aby utworzyć prawidłową kopię tabeli skrótu.

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 wejdą zmiany w tej tabeli skrótu, 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() polecenia , aby utworzyć płytkią 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), obiekty zagnieżdżone nadal będą wskazywać siebie nawzajem.

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ótów). 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 , takiego 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 zakryłem dużo ziemi. Mam nadzieję, że odchodzisz odchylenie czegoś nowego lub zrozumienie go lepiej za każdym razem, gdy to czytasz. 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.