Udostępnij za pośrednictwem


Wszystko, co chciałeś wiedzieć o tablicach

Tablice to podstawowa cecha większości języków programowania. Są one kolekcją wartości lub obiektów, które są trudne do uniknięcia. Przyjrzyjmy się bliżej tablicom i wszystkim, co mają do zaoferowania.

Uwaga

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

Co to jest tablica?

Zaczynam od podstawowego opisu technicznego, czym są tablice i jak są one używane przez większość języków programowania, zanim przeniosę się na inne sposoby, w jaki program PowerShell korzysta z nich.

Tablica to struktura danych, która służy jako kolekcja wielu elementów. Możesz iterować po tablicy lub uzyskiwać dostęp do poszczególnych elementów przy użyciu indeksu. Tablica jest tworzona jako sekwencyjny fragment pamięci, w którym każda wartość jest przechowywana bezpośrednio obok drugiej.

Omówię każdy z tych szczegółów w miarę postępu.

Podstawowy sposób użycia

Ponieważ tablice są tak podstawową funkcją programu PowerShell, istnieje prosta składnia do pracy z nimi w programie PowerShell.

Utwórz tablicę

Pustą tablicę można utworzyć przy użyciu @()

PS> $data = @()
PS> $data.Count
0

Możemy utworzyć tablicę i umieścić ją z wartościami, umieszczając je w nawiasach @().

PS> $data = @('Zero','One','Two','Three')
PS> $data.Count
4

PS> $data
Zero
One
Two
Three

Ta tablica zawiera 4 elementy. Po wywołaniu zmiennej $data zostanie wyświetlona lista naszych elementów. Jeśli jest to tablica ciągów znaków, otrzymujemy jeden wiersz na ciąg.

Możemy zadeklarować tablicę w wielu wierszach kodu. Przecinek jest opcjonalny w tym przypadku i ogólnie pominięty.

$data = @(
    'Zero'
    'One'
    'Two'
    'Three'
)

Wolę deklarować moje tablice w ten sposób, w wielu wierszach. Nie tylko ułatwia odczytywanie wielu elementów, ale także ułatwia porównywanie z poprzednimi wersjami podczas korzystania z kontroli źródła.

Inna składnia

Powszechnie rozumie się, że @() jest składnią tworzenia tablicy, ale listy rozdzielone przecinkami działają przez większość czasu.

$data = 'Zero','One','Two','Three'

Write-Output do tworzenia tablic

Jedną z fajnych małych sztuczek, o których warto wspomnieć, jest to, że możesz użyć Write-Output, aby szybko tworzyć ciągi znaków w konsoli.

$data = Write-Output Zero One Two Three

Jest to przydatne, ponieważ nie trzeba umieszczać cudzysłowów wokół ciągów, gdy parametr akceptuje ciągi. Nigdy bym tego nie zrobił w skrypcie, ale w konsoli to już co innego.

Uzyskiwanie dostępu do elementów

Teraz, gdy masz tablicę z elementami, możesz chcieć uzyskać dostęp do tych elementów i zaktualizować je.

Przesunięcie

Aby uzyskać dostęp do poszczególnych elementów, użyjemy nawiasów [] z wartością przesunięcia zaczynającą się od 0. W ten sposób uzyskamy pierwszy element w naszej tablicy:

PS> $data = 'Zero','One','Two','Three'
PS> $data[0]
Zero

Powodem, dla którego używamy zera, jest to, że pierwszy element znajduje się na początku listy, więc używamy przesunięcia 0 elementów, aby uzyskać do niego dostęp. Aby przejść do drugiego elementu, musimy użyć przesunięcia 1, aby pominąć pierwszy element.

PS> $data[1]
One

Oznaczałoby to, że ostatni element znajduje się na przesunięciu 3.

PS> $data[3]
Three

Indeks

Teraz możesz zobaczyć, dlaczego wybrałem wartości, które wybrałem w tym przykładzie. Wprowadziłem to jako przesunięcie, ponieważ tym właśnie jest, ale to przesunięcie jest częściej nazywane indeksem. Indeks rozpoczynający się od 0. W pozostałej części tego artykułu będę nazywał przesunięcie indeksem.

Specjalne sztuczki dotyczące indeksu

W większości języków można określić tylko jedną liczbę jako indeks i otrzymać pojedynczy element. Program PowerShell jest znacznie bardziej elastyczny. Jednocześnie można użyć wielu indeksów. Podając listę indeksów, możemy wybrać kilka elementów.

PS> $data[0,2,3]
Zero
Two
Three

Elementy są zwracane na podstawie kolejności podanych indeksów. Jeśli zduplikujesz indeks, otrzymasz ten element za każdym razem.

PS> $data[3,0,3]
Three
Zero
Three

Możemy określić sekwencję liczb z wbudowanym operatorem ...

PS> $data[1..3]
One
Two
Three

Działa to również odwrotnie.

PS> $data[3..1]
Three
Two
One

Wartości indeksu ujemnego można użyć do przesunięcia od końca. Jeśli więc potrzebujesz ostatniego elementu na liście, możesz użyć -1.

PS> $data[-1]
Three

Jedno ostrzeżenie dotyczące operatora ... Sekwencja 0..-1 i -1..0 oblicza wartości 0,-1 i -1,0. Łatwo jest zobaczyć $data[0..-1] i pomyśleć, że wyliczy wszystkie elementy, jeśli zapomnisz o tym szczególe. $data[0..-1] daje tę samą wartość co $data[0,-1], zawierając tylko pierwszy i ostatni element z tablicy, pomijając wszystkie inne wartości. Oto większy przykład:

PS> $a = 1,2,3,4,5,6,7,8
PS> $a[2..-1]
3
2
1
8

Jest to takie samo, jak:

PS> $a[2,1,0,-1]
3
2
1
8

Poza granicami

W większości języków, jeśli próbujesz uzyskać dostęp do indeksu elementu, który znajduje się obok końca tablicy, wystąpi błąd lub wyjątek. Program PowerShell w trybie dyskretnym nie zwraca niczego.

PS> $null -eq $data[9000]
True

Nie można indeksować tablicy, ponieważ jest null

Jeśli zmienna jest $null i próbujesz ją zaindeksować jak tablicę, otrzymasz wyjątek System.Management.Automation.RuntimeException z komunikatem Cannot index into a null array.

PS> $empty = $null
PS> $empty[0]
Error: Cannot index into a null array.

Przed próbą uzyskania dostępu do elementów wewnątrz nich upewnij się, że tablice nie są $null.

Liczba

Tablice i inne kolekcje mają właściwość Count, która informuje, ile elementów znajduje się w tablicy.

PS> $data.Count
4

Program PowerShell 3.0 dodał właściwość Count do większości obiektów. Możesz mieć pojedynczy obiekt i powinien dać liczbę 1.

PS> $date = Get-Date
PS> $date.Count
1

Nawet $null ma właściwość Count, z wyjątkiem tego, że zwraca 0.

PS> $null.Count
0

Istnieją tu pułapki, do których wrócę podczas sprawdzania $null lub pustych tablic w dalszej części tego artykułu.

Błędy poza jednym

Częstym błędem programistycznym jest rozpoczynanie tablic od indeksu 0. Błędy związane z przesunięciem o jeden mogą być wprowadzone na dwa sposoby.

Po pierwsze, myśląc, że chcesz drugiego elementu i używając indeksu 2, naprawdę otrzymasz trzeci element. Lub myśląc, że masz cztery elementy i chcesz uzyskać dostęp do ostatniego, używasz numeracji, aby się do niego dostać.

$data[ $data.Count ]

Program PowerShell z łatwością to robi i wskaże dokładnie, który element znajduje się na indeksie 4: $null. Należy użyć $data.Count - 1 lub -1, o których dowiedzieliśmy się powyżej.

PS> $data[ $data.Count - 1 ]
Three

W tym miejscu możesz użyć indeksu -1, aby uzyskać ostatni element.

PS> $data[ -1 ]
Three

Lee Dailey zwrócił mi również uwagę na to, że możemy użyć $data.GetUpperBound(0), aby uzyskać maksymalny numer indeksu.

PS> $data.GetUpperBound(0)
3
PS> $data[ $data.GetUpperBound(0) ]
Three

Drugim z najczęstszych sposobów jest iterowanie po liście i niezatrzymanie się w odpowiednim momencie. Omówię to ponownie, gdy mówimy o używaniu pętli for.

Aktualizowanie elementów

Możemy użyć tego samego indeksu, aby zaktualizować istniejące elementy w tablicy. Daje nam to bezpośredni dostęp do aktualizowania poszczególnych elementów.

$data[2] = 'dos'
$data[3] = 'tres'

Jeśli spróbujemy zaktualizować element, który znajduje się za ostatnim elementem, otrzymujemy błąd Index was outside the bounds of the array..

PS> $data[4] = 'four'
Index was outside the bounds of the array.
At line:1 char:1
+ $data[4] = 'four'
+ ~~~~~~~~~~~~~
+ CategoryInfo          : OperationStopped: (:) [], IndexOutOfRangeException
+ FullyQualifiedErrorId : System.IndexOutOfRangeException

Wrócę do tego później, kiedy będę mówić o tym, jak zwiększyć tablicę.

Iteracja

W pewnym momencie może być konieczne przejście przez całą listę lub iterowanie jej i wykonanie określonej akcji dla każdego elementu w tablicy.

Rurociąg

Tablice i potok programu PowerShell są przeznaczone dla siebie nawzajem. Jest to jeden z najprostszych sposobów przetwarzania tych wartości. Po przekazaniu tablicy do potoku każdy element wewnątrz tablicy jest przetwarzany indywidualnie.

PS> $data = 'Zero','One','Two','Three'
PS> $data | ForEach-Object {"Item: [$PSItem]"}
Item: [Zero]
Item: [One]
Item: [Two]
Item: [Three]

Jeśli nie widziałeś wcześniej $PSItem, po prostu wiesz, że jest to to samo, co $_. Można użyć obu, ponieważ każda z nich reprezentuje bieżący obiekt w potoku.

Pętla ForEach

Pętla foreach działa dobrze z kolekcjami. Używanie składni: foreach ( <variable> in <collection> )

foreach ( $node in $data )
{
    "Item: [$node]"
}

metoda ForEach

Mam tendencję do zapominania o tym, ale działa dobrze w przypadku prostych operacji. Program PowerShell umożliwia wywoływanie ForEach() w kolekcji.

PS> $data.ForEach({"Item [$PSItem]"})
Item [Zero]
Item [One]
Item [Two]
Item [Three]

ForEach() przyjmuje blok skryptu jako parametr. Nawiasy można usunąć i po prostu podać blok skryptu.

$data.ForEach{"Item [$PSItem]"}

Jest to mniej znana składnia, ale działa tak samo. Ta metoda ForEach została dodana w programie PowerShell 4.0.

Pętla for

Pętla for jest używana w większości innych języków, ale nie widzisz jej zbyt wiele w programie PowerShell. Gdy to zobaczysz, często jest to w kontekście iterowania tablicy.

for ( $index = 0; $index -lt $data.Count; $index++)
{
    "Item: [{0}]" -f $data[$index]
}

Pierwszą rzeczą, jaką robimy, jest zainicjowanie $index na 0. Następnie dodamy warunek, który $index musi być mniejszy niż $data.Count. Na koniec określamy, że za każdym razem, gdy zapętlamy, musimy zwiększyć indeks o 1. W tym przypadku $index++ jest skrótem od $index = $index + 1. Operator formatu (-f) służy do wstawiania wartości $data[$index] w ciągu wyjściowym.

Zawsze, gdy używasz pętli for, zwróć szczególną uwagę na warunek. Użyłem $index -lt $data.Count tutaj. Łatwo jest popełnić niewielki błąd w warunku, co prowadzi do błędu o jeden w logice. Używanie $index -le $data.Count lub $index -lt ($data.Count - 1) jest nieco nieprawidłowe. Spowodowałoby to przetworzenie zbyt wielu lub zbyt mało elementów. Jest to klasyczny błąd przesunięcia o jedną wartość.

Pętla przełącznika

Jest to taki, który jest łatwy do przeoczenia. Jeśli podasz tablicę do instrukcji switch, sprawdza każdy element w tablicy.

$data = 'Zero','One','Two','Three'
switch( $data )
{
    'One'
    {
        'Tock'
    }
    'Three'
    {
        'Tock'
    }
    Default
    {
        'Tick'
    }
}
Tick
Tock
Tick
Tock

Istnieje wiele ciekawych rzeczy, które możemy zrobić za pomocą instrukcji switch. Mam inny artykuł poświęcony temu.

Aktualizowanie wartości

Gdy tablica jest kolekcją ciągów znaków lub liczb całkowitych (typy wartościowe), czasami możesz chcieć zaktualizować wartości w tablicy podczas ich iteracji. Większość powyższych pętli używa zmiennej w pętli, która przechowuje kopię wartości. W przypadku zaktualizowania tej zmiennej oryginalna wartość w tablicy nie zostanie zaktualizowana.

Wyjątkiem od tej instrukcji jest pętla for. Jeśli chcesz przejść przez tablicę i zaktualizować wartości w niej, pętla for to dokładnie to, czego potrzebujesz.

for ( $index = 0; $index -lt $data.Count; $index++ )
{
    $data[$index] = "Item: [{0}]" -f $data[$index]
}

Ten przykład przyjmuje wartość według indeksu, wprowadza kilka zmian, a następnie używa tego samego indeksu, aby przypisać go z powrotem.

Tablice obiektów

Do tej pory jedyną rzeczą, którą umieściliśmy w tablicy, jest typ wartości, ale tablice mogą również zawierać obiekty.

$data = @(
    [pscustomobject]@{FirstName='Kevin';LastName='Marquette'}
    [pscustomobject]@{FirstName='John'; LastName='Doe'}
)

Wiele poleceń cmdlet zwraca kolekcje obiektów jako tablice podczas przypisywania ich do zmiennej.

$processList = Get-Process

Wszystkie podstawowe cechy, o których już mówiliśmy, mają zastosowanie do tablic obiektów, uwzględniając kilka szczegółów, na które warto zwrócić uwagę.

Uzyskiwanie dostępu do właściwości

Możemy użyć indeksu, aby uzyskać dostęp do pojedynczego elementu w kolekcji, podobnie jak w przypadku typów wartości.

PS> $data[0]

FirstName LastName
-----     ----
Kevin     Marquette

Możemy uzyskać bezpośredni dostęp do właściwości i aktualizować je.

PS> $data[0].FirstName

Kevin

PS> $data[0].FirstName = 'Jay'
PS> $data[0]

FirstName LastName
-----     ----
Jay       Marquette

Właściwości tablicy

Zwykle należy wyliczyć całą listę, taką jak ta, aby uzyskać dostęp do wszystkich właściwości:

PS> $data | ForEach-Object {$_.LastName}

Marquette
Doe

Lub poprzez użycie polecenia cmdlet Select-Object -ExpandProperty.

PS> $data | Select-Object -ExpandProperty LastName

Marquette
Doe

Jednak program PowerShell oferuje nam możliwość żądania LastName bezpośrednio. Program PowerShell wylicza je wszystkie dla nas i zwraca czystą listę.

PS> $data.LastName

Marquette
Doe

Wyliczenie wciąż się odbywa, ale nie widzimy jego złożoności.

filtrowanie Where-Object

W tym miejscu znajduje się Where-Object, dzięki czemu możemy filtrować i wybierać, czego chcemy użyć z tablicy na podstawie właściwości obiektu.

PS> $data | Where-Object {$_.FirstName -eq 'Kevin'}

FirstName LastName
-----     ----
Kevin     Marquette

Możemy napisać taki sam zestaw zapytań, aby uzyskać FirstName, którego szukamy.

$data | where FirstName -EQ Kevin

Where()

Tablice mają metodę Where(), która umożliwia określenie filtru za pomocą scriptblock.

$data.Where({$_.FirstName -eq 'Kevin'})

Ta funkcja została dodana w programie PowerShell 4.0.

Aktualizowanie obiektów w pętlach

W przypadku typów wartości jedynym sposobem aktualizacji tablicy jest użycie pętli for, ponieważ musimy znać indeks, aby zastąpić wartość. Mamy więcej opcji z obiektami, ponieważ są to typy odwołań. Oto szybki przykład:

foreach($person in $data)
{
    $person.FirstName = 'Kevin'
}

Ta pętla iteruje przez każdy obiekt w tablicy $data. Ponieważ obiekty są typami referencyjnymi, zmienna $person odwołuje się do dokładnie tego samego obiektu, który znajduje się w tablicy. Dlatego aktualizacje jego właściwości aktualizują oryginał.

Nadal nie można zamienić całego obiektu w ten sposób. Jeśli spróbujesz przypisać nowy obiekt do zmiennej $person, aktualizujesz odwołanie do zmiennej innej, która nie wskazuje już oryginalnego obiektu w tablicy. Nie działa to tak, jakby można było oczekiwać:

foreach($person in $data)
{
    $person = [pscustomobject]@{
        FirstName='Kevin'
        LastName='Marquette'
    }
}

Operatorów

Operatory w programie PowerShell działają również na tablicach. Niektóre z nich działają nieco inaczej.

-dołączyć

Operator -join jest najbardziej oczywisty, więc najpierw przyjrzyjmy się temu. Lubię operator -join i często go używam. Łączy wszystkie elementy w tablicy za pomocą znaku lub ciągu, który podasz.

PS> $data = @(1,2,3,4)
PS> $data -join '-'
1-2-3-4
PS> $data -join ','
1,2,3,4

Jedną z funkcji, które lubię na temat operatora -join jest obsługa pojedynczych elementów.

PS> 1 -join '-'
1

Używam tego w rejestrowaniu i rozbudowanych komunikatach.

PS> $data = @(1,2,3,4)
PS> "Data is $($data -join ',')."
Data is 1,2,3,4.

-join $array

Oto sprytna sztuczka, którą Lee Dailey zwrócił do mnie uwagę. Jeśli kiedykolwiek zechcesz połączyć wszystko bez użycia ogranicznika, zamiast robić to w ten sposób:

PS> $data = @(1,2,3,4)
PS> $data -join $null
1234

Można użyć -join z tablicą jako parametru bez prefiksu. Spójrz na ten przykład, aby zobaczyć, o czym mówię.

PS> $data = @(1,2,3,4)
PS> -join $data
1234

-replace i -split

Inne operatory, takie jak -replace i -split są wykonywane na każdym elemencie w tablicy. Nie mogę powiedzieć, że kiedykolwiek używałem ich w ten sposób, ale oto przykład.

PS> $data = @('ATX-SQL-01','ATX-SQL-02','ATX-SQL-03')
PS> $data -replace 'ATX','LAX'
LAX-SQL-01
LAX-SQL-02
LAX-SQL-03

-zawiera

Operator -contains umożliwia sprawdzenie tablicy wartości, aby sprawdzić, czy zawiera określoną wartość.

PS> $data = @('red','green','blue')
PS> $data -contains 'green'
True

-w

Jeśli masz pojedynczą wartość, którą chcesz zweryfikować, pasuje do jednej z kilku wartości, możesz użyć operatora -in. Wartość będzie znajdować się po lewej stronie, a tablica po prawej stronie operatora.

PS> $data = @('red','green','blue')
PS> 'green' -in $data
True

Może to być kosztowne, jeśli lista jest duża. Często używam wzorca wyrażenia regularnego, jeśli sprawdzam więcej niż kilka wartości.

PS> $data = @('red','green','blue')
PS> $pattern = "^({0})$" -f ($data -join '|')
PS> $pattern
^(red|green|blue)$

PS> 'green' -match $pattern
True

-eq i -ne

Równość i tablice mogą być skomplikowane. Kiedy tablica znajduje się po lewej stronie, każdy z jej elementów jest porównywany. Zamiast zwracać True, zwraca obiekt, który jest zgodny.

PS> $data = @('red','green','blue')
PS> $data -eq 'green'
green

Gdy używasz operatora -ne, otrzymujemy wszystkie wartości, które nie są równe naszej wartości.

PS> $data = @('red','green','blue')
PS> $data -ne 'green'
red
blue

Jeśli używasz tego w instrukcji if(), zwracana wartość jest wartością True. Jeśli żadna wartość nie zostanie zwrócona, jest to wartość False. Oba te następne wyrażenia przyjmują wartość True.

$data = @('red','green','blue')
if ( $data -eq 'green' )
{
    'Green was found'
}
if ( $data -ne 'green' )
{
    'And green was not found'
}

Wrócę do tego za chwilę, kiedy będziemy rozmawiać o testowaniu dla $null.

-mecz

Operator -match próbuje dopasować każdy element w kolekcji.

PS> $servers = @(
    'LAX-SQL-01'
    'LAX-API-01'
    'ATX-SQL-01'
    'ATX-API-01'
)
PS> $servers -match 'SQL'
LAX-SQL-01
ATX-SQL-01

Jeśli używasz -match z jedną wartością, specjalna zmienna $Matches zostanie wypełniona informacjami o dopasowaniu. Nie jest tak, gdy tablica jest przetwarzana w ten sposób.

Możemy podjąć to samo podejście przy użyciu Select-String.

$servers | Select-String SQL

Przyjrzę się bliżej Select-String,-match i zmiennej $Matches w innym wpisie zatytułowanym : Wiele sposobów na użycie regex.

$null lub puste

Testowanie $null lub pustych tablic może być trudne. Oto typowe pułapki z tablicami.

Na pierwszy rzut oka to stwierdzenie wygląda na to, że powinno działać.

if ( $array -eq $null)
{
    'Array is $null'
}

Ale po prostu przeszedłem, jak -eq sprawdza każdy element w tablicy. W związku z tym możemy mieć tablicę kilku elementów z pojedynczą wartością $null i zostanie ona zinterpretowana jako $true.

$array = @('one',$null,'three')
if ( $array -eq $null)
{
    'I think Array is $null, but I would be wrong'
}

Dlatego najlepszym rozwiązaniem jest umieszczenie $null po lewej stronie operatora. To sprawia, że ten scenariusz nie jest problemem.

if ( $null -eq $array )
{
    'Array actually is $null'
}

Tablica $null nie jest taka sama jak pusta tablica. Jeśli wiesz, że masz tablicę, rozważ sprawdzenie liczby obiektów w niej. Jeśli tablica to $null, licznik to 0.

if ( $array.Count -gt 0 )
{
    "Array isn't empty"
}

Jest jeszcze jedna pułapka, na którą trzeba uważać. Można użyć Count, nawet jeśli masz jeden obiekt, chyba że ten obiekt jest PSCustomObject. Jest to usterka usunięta w programie PowerShell 6.1. To dobra wiadomość, ale wiele osób jest jeszcze na 5.1 i powinni uważać.

PS> $object = [pscustomobject]@{Name='TestObject'}
PS> $object.Count
$null

Jeśli nadal używasz programu PowerShell 5.1, możesz opakowować obiekt w tablicy przed sprawdzeniem liczby, aby uzyskać dokładną liczbę.

if ( @($array).Count -gt 0 )
{
    "Array isn't empty"
}

Aby w pełni grać bezpiecznie, sprawdź $null, a następnie sprawdź liczbę.

if ( $null -ne $array -and @($array).Count -gt 0 )
{
    "Array isn't empty"
}

Wszystkie -eq

Niedawno widziałem kogoś na Reddit zapytać, jak sprawdzić, czy każda wartość w tablicy pasuje do danej wartości. Użytkownik Reddit u/bis miał to sprytne rozwiązanie, które sprawdza nieprawidłowe wartości, a następnie przerzuca wynik.

$results = Test-Something
if ( -not ( $results -ne 'Passed') )
{
    'All results a Passed'
}

Dodawanie do tablic

Na tym etapie zaczynasz zastanawiać się, jak dodać elementy do tablicy. Szybka odpowiedź polega na tym, że nie można. Tablica ma stały rozmiar w pamięci. Jeśli musisz go rozwinąć lub dodać do niego pojedynczy element, musisz utworzyć nową tablicę i skopiować wszystkie wartości ze starej tablicy. Brzmi to jak wiele pracy, jednak program PowerShell ukrywa złożoność tworzenia nowej tablicy. Program PowerShell obsługuje operator dodawania (+) dla tablic.

Uwaga

Program PowerShell nie implementuje operacji odejmowania. Jeśli potrzebujesz elastycznej alternatywy dla tablicy, musisz użyć obiektu generycznego List.

Dodawanie tablic

Możemy użyć operatora dodawania z tablicami, aby utworzyć nową tablicę. Biorąc więc pod uwagę te dwie tablice:

$first = @(
    'Zero'
    'One'
)
$second = @(
    'Two'
    'Three'
)

Możemy dodać je razem, aby uzyskać nową tablicę.

PS> $first + $second

Zero
One
Two
Three

Plus równa się +=

Możemy utworzyć nową tablicę i dodać do niej element w następujący sposób:

$data = @(
    'Zero'
    'One'
    'Two'
    'Three'
)
$data += 'four'

Pamiętaj, że za każdym razem, gdy używasz +=, że duplikujesz i tworzysz nową tablicę. Nie jest to problem w przypadku małych zestawów danych, ale jest bardzo słabo skalowany.

Przypisanie sieci przesyłowej

Wyniki dowolnego pipeline'u można przypisać do zmiennej. Jest to tablica, jeśli zawiera wiele elementów.

$array = 1..5 | ForEach-Object {
    "ATX-SQL-$PSItem"
}

Zwykle, gdy myślimy o używaniu potoku, myślimy o typowych wierszach programu PowerShell. Możemy wykorzystać potok przetwarzania za pomocą instrukcji foreach() i innych pętli. Dlatego zamiast dodawać elementy do tablicy w pętli, możemy upuszczać elementy do potoku.

$array = foreach ( $node in (1..5))
{
    "ATX-SQL-$node"
}

Typy tablic

Domyślnie tablica w programie PowerShell jest tworzona jako typ [psobject[]]. Dzięki temu może zawierać dowolny typ obiektu lub wartości. Działa to, ponieważ wszystko jest dziedziczone z typu PSObject.

Silnie typizowane tablice

Tablicę dowolnego typu można utworzyć przy użyciu podobnej składni. Podczas tworzenia silnie typizowanej tablicy może ona zawierać wyłącznie wartości lub obiekty określonego typu.

PS> [int[]] $numbers = 1,2,3
PS> [int[]] $numbers2 = 'one','two','three'
ERROR: Cannot convert value "one" to type "System.Int32". Input string was not in a correct format."

PS> [string[]] $strings = 'one','two','three'

ArrayList

Dodawanie elementów do tablicy jest jednym z największych ograniczeń, ale istnieje kilka innych kolekcji, do których możemy się zwrócić, aby rozwiązać ten problem.

ArrayList jest często jedną z pierwszych rzeczy, o których myślimy, gdy potrzebujemy tablicy, z którą można szybciej pracować. Działa jak tablica obiektów wszędzie tam, gdzie go potrzebujemy, ale szybko obsługuje dodawanie elementów.

Poniżej przedstawiono sposób tworzenia ArrayList i dodawania do niego elementów.

$myarray = [System.Collections.ArrayList]::new()
[void]$myArray.Add('Value')

Odwołujemy się do platformy .NET, aby uzyskać ten typ. W tym przypadku używamy domyślnego konstruktora do jego utworzenia. Następnie wywołujemy metodę Add, aby dodać do niej element.

Powodem, dla którego używam [void] na początku wiersza, jest pominięcie kodu powrotnego. Niektóre wywołania platformy .NET to robią i mogą utworzyć nieoczekiwane dane wyjściowe.

Jeśli jedynymi danymi, które masz w tablicy, są ciągi, zapoznaj się również z użyciem StringBuilder. Jest to prawie takie samo, ale ma kilka metod, które są przeznaczone tylko do pracy z ciągami. StringBuilder jest specjalnie zaprojektowany pod kątem wydajności.

Często ludzie przenoszą się z tablic do ArrayList. Jednak pochodzi to z czasu, w którym język C# nie miał ogólnego wsparcia. ArrayList jest wycofywany na rzecz ogólnego List[]

Lista ogólna

Typ ogólny to specjalny typ w języku C#, który definiuje uogólnioną klasę, a użytkownik określa typy danych, których używa podczas tworzenia. Jeśli więc chcesz wyświetlić listę liczb lub ciągów, zdefiniuj listę typów int lub string.

Oto jak utworzyć listę dla ciągów znaków.

$mylist = [System.Collections.Generic.List[string]]::new()

Lub lista liczb.

$mylist = [System.Collections.Generic.List[int]]::new()

Możemy rzutować istniejącą tablicę na listę podobną do tej bez uprzedniego utworzenia obiektu:

$mylist = [System.Collections.Generic.List[int]]@(1,2,3)

Składnię można skrócić za pomocą instrukcji using namespace w programie PowerShell 5 i nowszych. Instrukcja using musi być pierwszym wierszem skryptu. Deklarując przestrzeń nazw, program PowerShell umożliwia pozostawienie go poza typami danych podczas odwołowania się do nich.

using namespace System.Collections.Generic
$myList = [List[int]]@(1,2,3)

Dzięki temu List jest znacznie bardziej użyteczne.

Masz dostępną podobną metodę Add. W przeciwieństwie do metody ArrayList nie ma wartości zwracanej w metodzie Add, więc nie musimy jej void.

$myList.Add(10)

Nadal możemy uzyskiwać dostęp do elementów tak jak do innych tablic.

PS> $myList[-1]
10

List[psobject]

Możesz mieć listę dowolnego typu, ale jeśli nie znasz typu obiektów, możesz użyć [List[psobject]] do ich przechowywania.

$list = [List[psobject]]::new()

Usuń

ArrayList i generyczny List[] obsługują usuwanie elementów z kolekcji.

using namespace System.Collections.Generic
$myList = [List[string]]@('Zero','One','Two','Three')
[void]$myList.Remove("Two")
Zero
One
Three

Podczas pracy z typami wartości usuwa pierwszy z listy. Możesz wywoływać ją wielokrotnie, aby nadal usuwać tę wartość. Jeśli masz typy odwołań, musisz podać obiekt, który chcesz usunąć.

[List[System.Management.Automation.PSDriveInfo]]$drives = Get-PSDrive
$drives.Remove($drives[2])
$delete = $drives[2]
$drives.Remove($delete)

Metoda remove zwraca true, jeśli udało mu się znaleźć i usunąć element z kolekcji.

Więcej kolekcji

Istnieje wiele innych kolekcji, które mogą być używane, ale te są dobrymi ogólnymi zamiennikami tablic. Jeśli chcesz dowiedzieć się więcej o tych opcjach, zapoznaj się z tym Gist, który Mark Kraus przygotował.

Inne niuanse

Teraz, gdy omówiłem wszystkie główne funkcje, oto kilka innych rzeczy, które chciałem wspomnieć, zanim podsumuję to.

Tablice o z góry ustalonym rozmiarze

Wspomniałem, że nie można zmienić rozmiaru tablicy po jej utworzeniu. Możemy utworzyć tablicę wstępnie określonego rozmiaru, wywołując ją za pomocą konstruktora new($size).

$data = [Object[]]::new(4)
$data.Count
4

Mnożenie tablic

Ciekawym małym trikiem jest to, że można pomnożyć tablicę przez liczbę całkowitą.

PS> $data = @('red','green','blue')
PS> $data * 3
red
green
blue
red
green
blue
red
green
blue

Inicjowanie za pomocą 0

Typowym scenariuszem jest utworzenie tablicy ze wszystkimi zerami. Jeśli zamierzasz używać tylko liczb całkowitych, silnie typizowana tablica liczb całkowitych domyślnie zawiera same zera.

PS> [int[]]::new(4)
0
0
0
0

Możemy również użyć sztuczki mnożenia, aby to zrobić.

PS> $data = @(0) * 4
PS> $data
0
0
0
0

Dobrą stroną sztuczki mnożenia jest to, że można użyć dowolnej wartości. Więc jeśli wolisz mieć 255 jako wartość domyślną, byłoby to dobrym sposobem, aby to zrobić.

PS> $data = @(255) * 4
PS> $data
255
255
255
255

Zagnieżdżone tablice

Tablica wewnątrz tablicy nazywa się tablicą zagnieżdżoną. Nie używam tych elementów w programie PowerShell, ale używałem ich więcej w innych językach. Rozważ użycie tablic tablic, gdy dane układają się w wzorzec siatki.

Poniżej przedstawiono dwa sposoby tworzenia tablicy dwuwymiarowej.

$data = @(@(1,2,3),@(4,5,6),@(7,8,9))

$data2 = @(
    @(1,2,3),
    @(4,5,6),
    @(7,8,9)
)

Przecinek jest bardzo ważny w tych przykładach. Dałem wcześniejszy przykład normalnej tablicy w wielu wierszach, w których przecinek był opcjonalny. Nie tak jest w przypadku tablicy wielowymiarowej.

Sposób, w jaki używamy notacji indeksu, zmienia się teraz nieco, gdy mamy zagnieżdżoną tablicę. Korzystając z powyższego $data, w ten sposób uzyskujemy dostęp do wartości 3.

PS> $outside = 0
PS> $inside = 2
PS> $data[$outside][$inside]
3

Dodaj zestaw nawiasów dla każdego poziomu zagnieżdżania tablicy. Pierwszy zestaw nawiasów jest przeznaczony dla najbardziej zewnętrznej tablicy, a następnie przechodzisz do wewnątrz.

Write-Output -NoEnumerate

Program PowerShell lubi odpakowywać lub wyliczać tablice. Jest to podstawowy aspekt sposobu korzystania z potoku przez program PowerShell, ale czasami nie chcesz, aby tak się stało.

Często przekierowuję obiekty do Get-Member, aby dowiedzieć się o nich więcej. Gdy przekazuję do niej tablicę, zostanie ona rozpakowana i Get-Member zobaczy elementy tablicy, a nie rzeczywistą tablicę.

PS> $data = @('red','green','blue')
PS> $data | Get-Member
TypeName: System.String
...

Aby zapobiec odpakowaniu tablicy, możesz użyć Write-Output -NoEnumerate.

PS> Write-Output -NoEnumerate $data | Get-Member
TypeName: System.Object[]
...

Mam drugi sposób, który jest raczej trikiem (i staram się unikać takich trików). Przecinek można umieścić przed tablicą, zanim skierujesz ją do potoku. To zawija $data do innej tablicy, gdzie jest jedynym elementem, więc po rozpakowaniu tablicy zewnętrznej otrzymujemy rozpakowane $data.

PS> ,$data | Get-Member
TypeName: System.Object[]
...

Zwróć tablicę

Rozpakowywanie tablic odbywa się również, gdy przekazujesz lub zwracasz wartości z funkcji. Nadal można uzyskać tablicę, jeśli przypiszesz dane wyjściowe do zmiennej, więc nie jest to często problem.

Sęk w tym, że masz nową tablicę. Jeśli kiedykolwiek wystąpi problem, możesz użyć Write-Output -NoEnumerate $array lub return ,$array, aby obejść ten problem.

Czy coś jeszcze?

Wiem, że to wszystko to dużo do przyswojenia. Mam nadzieję, że nauczysz się czegoś z tego artykułu za każdym razem, gdy go czytasz i że okazuje się być dobrym odniesieniem dla Ciebie przez długi czas. Jeśli okaże się, że jest to przydatne, podziel się nim z innymi osobami, które uważasz, że może uzyskać z niego wartość.

Z tego miejsca polecam zapoznać się z podobnym postem, który napisałem o hashtables,.