Работа с массивом в Powershell и листами с примерами


02 октября 2019


Работа в Powershell с массивами и листами на примерах

Массивы в Powershell являются аналогами списков в Python. Легче всего воспринимать эти понятия как хранение в одной переменной множества значений. То есть это структура данных, которую можно перебирать и вызывать отдельный элемент используя индексы. Массивы, они же и множества и array, в Powershell и других языках программирования являются отдельным типом данных так же как числа и строки.

Создание

Массивы являются базовой возможностью Powershell, и их создание делается просто. Для создания нужно использовать символы @() :

$array = @()
$array.count

Создание массива в Powershell

Count (в переводе счетчик) - показывает сколько у нас элементов в массиве. Так как мы создали пустое множество он равен нулю.

Можно создать сразу со значениями. Что бы это выполнить нужно добавить значения, разделенные запятой, внутри @() :

$lst = @('Первый', 'Второй', 'Четвертый', 'Третий')
$lst.Count
$lst

powershell вывод массива

Написав $array мы вывели значения переменной. Каждое из четырех значений выводится с новой строчки. 

Другой способ создания - это писать значения с новой строчки. В этом случае запятые ставить не обязательно:

$lst = @('Первый'
            '2'
            'Третий'
            4)
$lst.Count
$lst

powershell элементов в массиве

 Хоть такая возможность и есть в Powershell, я бы не советовал использовать без запятых. В остальном, с использованием табуляции, такой синтаксис может улучшить читаемость (особенно если у нас сотни значений).

Еще один способ объявления - это не указывать скобки и знак @ :

$lst = '7',8,'Один','One'

powershell создать массив

Следующий вариант не используется на практике, но вы можете иметь его ввиду:

$lst = Write-Output Один 2 Трии '4'

powershell массивы

Обратите внимание, что число 4 стало строкой только тогда, когда ее поместили в кавычки.

 

Получение элементов

Как уже было показано выше получить весь массив можно написав переменную, но так же можно использовать другие методы как смещение и индексы.

Индексы

Когда нужно получить конкретный элемент мы указываем скобки []. Самое первое значение массива имеет идентификатор 0. На следующем примере мы получим первый объект:

$arraylist = 'Первый','Второй','Третий'
$arraylist[0]

Для получения следующих индексов просто измените число:

$arraylist[2]

powershell вывод массива

На примерах выше мы получили данные по их индексам. В отличие от большинства языков в Powershell мы можем указывать несколько индексов, которые хотим получить. Так же можно вызвать один и тот же элемент неограниченное количество раз:

$arraylist[0,2]

powershell массивы

Срез (slice) или последовательность - это когда мы получаем данные с одного индекса по другой. Последовательности обозначаются двумя точками '..' . Для примера получим первые три значения:

$arraylist = @('admin@login', 'admin', 'Password', '20.02.2004', '????', '????')
$arraylist[0..2]
$arraylist[1..100]

powershell массивы

Как видно мы можем получить данные не в строгой последовательности относительно массива. Так же указав несуществующий индекс '100' мы не получили ошибку, а вывели все значения с первого до последнего. Если вызвать единственный элемент, который не будет существовать, то значения будут равны Null.

$null -eq $arraylist[200]

В большинстве языков отрицательное число '-1' обозначает последний объект массива. Обратите внимание, что использование [-1..0] и [0..-1] приведет только к получению первого и последнего элемента:

$arraylist[0..-1]
$arraylist[-1..0]

powershell создать массив

Можно указать -2 для получения предпоследнего объекта и т.д. Последний объект можно получить и таким образом:

$arraylist.GetUpperBound(0)

Если вы попытаетесь получить индекс у несуществующего массива, или с несуществующими значениями, то это может привести к разным последствиям:

$arraylist = $null,$null
$arraylist[0]
$arraylist = $null
$arraylist[0]

powershell array

Возможные ошибки:

  • Не удается индексировать в массив NULL.
  • Cannot index into a null array.

Если вы не совсем понимаете как работает NULL в Powershell, то советовал бы вам прочитать предыдущую статью.

Использование счетчика Count

Счетчик удобно использовать, когда мы хотим проверить количество элементов в массиве:

$lst = '0','2','3','4'
$lst.count

if ($lst.count -eq 4){Write-Output 'Hacked!'}

powershell элементов в массиве

Используя счетчик можно получить и элемент массива. Если вы никогда не работали с индексами обращайте внимание, что счетчик возвращает количество элементов в массиве, но индексация начинается с 0 элемента. То есть для получения последнего элемента нам нужно вычесть 1:

$lst[$lst.count - 1]
$lst[0..($lst.count - 1)]
$lst[2,($lst.count - 1)]

powershell string array 

Если вы не закроете в скобки выражение в случае получения среза, то будут следующие ошибки:

  • Сбой вызова метода из-за отсутствия в [System.Object[]] метода с именем "op_Subtraction".
  • Method invocation failed because [System.Object[]] does not contain a method named 'op_Subtraction'.

Замена элементов

Для замены элементов так же нужно указать индекс. Если мы хотим заменить первый объект, то соответственно нужно указать индекс 0:

$data = @(7,5,8,4,5)
$data[0]=0
$data
$data[-1]='Пять'
$data

powershell arraylist

Указание несуществующего индекса вызовет ошибки:

  • Index was outside the bounds of the array
  • Индекс находился вне границ массива.

 

Итерации

Кроме индексов с массивом часто приходится работать используя циклы и другие методы перечисления. Далее будет рассказано об этом.

Конвейер или Pipeline

В примере ниже мы проверим доступность хостов передав каждый объект массива через конвейер:

$sites = 'localhost','fixmypc.ru'
$sites | ForEach-Object {ping $_}

powershell массив объектов

Цикл ForEach

В циклах массивы используются похоже:

foreach ($site in $sites)
{
ping $site
}

powershell массивы

Так же доступен метод foreach, который немного отличается синтаксисом, но работает так же:

$sites.foreach({ping $_})

Использование switch

Switch это аналог условий, который объединен в единую конструкцию. Мы можем объявить switch и передать в наш список:

$data = 'folder','hack'
switch( $data )
{
'folder' {'Создать папку с именем'; New-Item -Path 'C:\Array' -ItemType Directory}
'file' {'Создать файл'}
'hack' {'Взломать аську'; ping fixmypc.ru}
Default {'Ничего не создавать'}
}

powershell array

Обновление значений через циклы

Если нам понадобится обновить весь массив мы обратимся к циклам и мы можем столкнуться с трудностями. Проблемы заключаются в том, что в цикле находится не сам массив, а его значения и мы не сможем их изменить так. Обойти эту ситуацию можно двумя путями.

Первый способ - это обратиться к каждому значению по индексу, а саму 'длину' индексов измерить методами, которые описаны выше:

$val = @(1,2,3)
0..($val.Count-1) | foreach {$val[$_]=$val[$_] + 1}

powershell массивВ Powershell мы можем генерировать список из чисел указав только первое и последнее число и именно это я делал в следующей строке:

0..($val.Count-1)

В другой строке я указал заменить число под этим индексом на новое прибавив 1:

$val[$_]=$val[$_] + 1

Еще один способ через 'for', который практически не применяется PS:

for ( $index = 0; $index -lt $array.count; $index++ )
{
$array[$index] = ($array[$index] + 1) -f $array[$index]
}

powershell создать массив

 

Использование объектов в массиве

В предыдущих примерах мы рассматривали работу с типами данных, но массивы могут хранить любые данные и объекты. Такие методы удобно использовать, когда собираете много данных и часто с ними работаете:

$data = @(
[pscustomobject]@{Computer='localhost';Login='admin';Process=Get-Process -ComputerName 'localhost'},
[pscustomobject]@{Computer='127.0.0.1';Login='lizun';Process=Get-Process -ComputerName '127.0.0.1'}
)
$data

powershell массив объектов

Вызвав переменную вы увидите, что представление данных почти не отличается от вывода командлетов.

Для получения данных мы можем указывать индексы:

$data[0]

После получения объекта вызвать свойство ('колонку' с примера выше), которое нам нужно:

$data[0].Computer
$data[0].Process

powershell массив объектов

Когда нужно будет получить, например, все имена компьютеров используем циклы:

$data | ForEach-Object {$_.Computer}

Можно вызвать напрямую или командлет Select-Object:

$data | select Computer
$data.Computer

powershell массив объектов

 

Фильтрация массивов через Where-Object

Фильтрация можно использовать, например, когда нам нужно найти все цифры из массива наполненным разными данными. На следующем варианте видно, что она мало чем отличается от того, что мы применяем в командлетах:

$list = @(
[pscustomobject]@{Name='Dima';SubName='Penkin'},
[pscustomobject]@{Name='Valera';SubName='Lerov'}
)
$list | where {$_.Name -like 'D*' -and $_.SubName -like '*n'}
$list | Where-Object SubName -eq 'Lerov'

powershell поиск в массиве

Аналогично способу выше есть метод, который фильтрует так же:

$list.Where({$_.Name -eq 'Dima'})

 

Обновление объектов через цикл

При использовании циклов таким способом мы будем обновлять значение Name во всех значениях массива:

foreach($iden in $list)
{
$iden.Name = 'Sasha'
}

powershell массивы замена

Если вы хотите заменить конкретное свойство, нужно использовать условия:

$list = @(
[pscustomobject]@{Name='Dima';SubName='Penkin'},
[pscustomobject]@{Name='Valera';SubName='Lerov'}
)
foreach($iden in $list){
if ($iden.Name -eq 'Dima'){$iden.Name='dd'}
}
$list

powershell массивы замена

Заменить весь объект используя цикл нельзя.

 

Операторы

-join

Join это оператор, который может преобразовать весь массив в единую строку где разделителем может быть любой символ:

$string_array = @('Masha', 'Sasha', 'Dima')
$string_array -join '.'
$string_array -join ' пошел к '

powershell строку в массив

Следующие варианты просто сольют все символы в одну строку без пробелов:

$string_array = @('Masha', 'Sasha', 'Dima')
$string_array -join $null
-join $string_array

 Можно использовать для логирования ошибок:

$data = @((Get-Date).Day, 'Error 5051', (hostname))
"Ошибка $data"

powershell двумерный массив

-replace

Операторы замены проверит каждый элемент и выполнят замену совпадающих значений:

$massive = 'Lena','Misha','Pika'
$massive -replace 'M','S'
$massive.Replace('L','p')

powershell массивы замена

Более подробно мы уже говорили о replace с Powershell.

-split

Split преобразует строку в список. В качестве разделителя я указал пробел:

$massive = 'Преобразование строки'
$massive -split ' '
$massive.Split(' ')

powershell строку в массив

-contains 

Этот оператор проверяет точное вхождение и возвращает булево значение True или False:

$list = 'Вася','Федя'
# Верно
$list -contains 'Вася'
# Не верно
'Вася' -contains $list

powershell поиск в массиве

-in

Отличия от contains в том, что он появился в Powershell 3 и создан для более удобной читаемости. В операторах Powershell важно соблюдать позиции того что мы ищем и где. Ошибки могут быть очень неявными. В случае с contains объект должен находиться слева, а в in справа:

$list = 'Вася','Федя'
# Верно
'Вася' -in $list
# Не верно
$list -in 'Вася'

powershell поиск в массиве

Этот оператор так же можно использовать для поиска в массиве Powershell.

 -eq и -ne

При использовании сравнения 'eq' у нас вернется либо само значение, либо False. В случае неравенства 'ne' вернутся либо остальные значения, либо True. Важно использовать искомое значение справа, а объект слева:

$data = @('один','два','три')
# Верно
$data -eq 'один'
$data -ne 'один'
# Не верно
'один' -eq $data
'один' -ne $data

powershell сравнение массива

Если нужно проверить, что все значения в листах совпадают требованиям, можно воспользоваться такой конструкцией:

$array = @('Ok','Ok','not Ok')
if ( -not ( $array -ne 'Ok') ){'Что-то не так, это False'}

$array = @('Ok','Ok','Ok')
if ( -not ( $array -ne 'Ok') ){'Все ок'}

powershell сравнение массива

-match и -like

Оператор match позволяет использовать регулярные выражения. Он будет искать каждое совпадение в массиве и вернет их:

$var = @('QQ-11-22','22-44-SS','FF-GG-HH')
# Верно
$var -match '22'
# Не верно
'22' -match $var

powershell поиск в массивеВ операторе like можно использовать знак *, который будет идентифицировать пропущенный текст. Следующий пример сработает с ошибкой в случае с match:

$var = @('QQ-11-22','22-44-SS','FF-GG-HH')
# Верно
$var -like '2*S'
# Не верно
'2*S' -like $var

powershell поиск в массивеПохоже сработает Select-String:

$var = @('QQ-11-22','22-44-SS','FF-GG-HH')
$var | Select-String 22

 

Проверка $null

Если мы будем проверять существующий массив, в котором нет значений, то следующий пример вернет False:

$foo = @()
if($foo -eq $null) { "Пустой" } else { "Значения есть" }

Он так же вернет False, если внутри будет $null:

$foo = @($null)
if($foo -eq $null) { "Пустой" } else { "Значения есть" }

Что бы избежать ошибок нужно добавлять счетчик:

if ( $foo -ne $null -and @($foo).count -gt 0 ){'Все по маслу'}

powershell массив $null

Сортировка массива

Для сортировки в Powershell используется командлет Sort-Object:

$sort = 4,7,3,2,4
$sort | Sort-Object
$sort = 'V','F','A'
$new_sort = $sort = 'V','F','A' | Sort-Object

Более подробно командлет Powershell Sort-Object уже рассматривался.

 

Добавление элементов в массив Powershell

Фактически Powershell не позволяет добавлять элементы в массив. Для того что бы это сделать массив сначала удаляется, а затем создается новый. Возможно кому-то будет более понятно это под "неизменяемый объект". Тем не менее процесс удаления и создания сильно упрощен в PS и вам не составит труда это сделать.

Сложение

Для создания нового массива можно указать оператор сложения для двух существующих:

$a = @('1')
$b = @('2','4')
$c = $a + $b
$c

Другой способ существует во множестве языках:

$array_add = @('1')
$array_add += 2
$array_add

powershell добавить в массив

Создание из конвейера

 Можно генерировать новый массив используя старый:

$array_add = @(0)
1..5 | ForEach-Object { $array_add+=$_ }
$array_add

powershell использование конвейера в массиве

 

Типы массивов

По умолчанию массив в Powershell создается типа [PSObject[]]. Это позволяет хранить любые типы данных. Если вы хотите хранить строгие типы данных, например только числа или определенный набор, то вы можете использовать следующие методы:

Массив из определенных типов данных

Используя следующий методы можно создать любой массив со строгими типами данных. Пример ниже позволяет использовать только числа:

[int[]] $num = 9,2,3,'4'

Обратите внимание, что 4 объявлена строковой, но она сама преобразуется в число без ошибок. В случае добавления букв будет ошибка:

  • Не удается преобразовать значение "s" в тип "System.Int32". Ошибка: "Входная строка имела неверный формат."
  • Cannot convert value "s" to type "System.Int32". Error: "Input string was not in a correct format."

Так же можно сделать и со строками, но в таком случае все добавленные данные преобразуются в строки:

[string[]] $str = '8','s','d'
$str+= 1

массив из строгих типов данных в Powershell

Универсальные списки

Универсальные списки относятся к C#, в котором нужно определять типы данных. Так мы создадим список из строк:

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

А так будет создан список из целых чисел:

$list = [System.Collections.Generic.List[int]]@(32,55,66)

Начиная с версии Powershell 5 можно сократить синтаксис используя using namespace. Using указывается в самом начале скрипта. 

using namespace System.Collections.Generic
$list = [List[int]]@(31,32,33)

Списки Powershell

Следующий синтаксис добавит число в список:

$list.Add(10)

Значения так же можно получать по индексам:

$list[-1]

Списки Powershell

Для удаления используется следующий синтаксис:

using namespace System.Collections.Generic
$var = [List[string]]@('Один','Два','Три')
[void]$var.Remove('Один')

Можно так же удалять по индексам:

$delete = $var[1]
$var.remove($delete)

Списки Powershell

List[PSObject]

Список из любого типа данных можно создать и так:

using namespace System.Collections.Generic
$list = [List[PSObject]]::new()

Списки Powershell

ArrayList

Для поддержки удаления, которого нет в обычных массивах, можно использовать ArrayList. Они так же быстрее работают. Для создания такого массива используйте следующий синтаксис:

$arraylist = [System.Collections.ArrayList]::new()
[void]$arraylist.Add('Someval')
$arraylist

Для удаления:

$arraylist.Remove('Someval')

 

Другие методы по работе с массивами

Кроме основных методов по работе с массивами есть методы, которые позволяют генерировать списки и создавать фиксированного размера. Далее разберем на примерах.

Фиксированный размер

В Powershell можно создать список с предопределенным размером:

$data = [Object[]]::new(5)
$data.count

Списки Powershell

Умножение

Так же как и в других языках массивы можно умножать:

$var = @('A','B','C')
$var*5

Похожий метод, который создаст 10 нулей:

[int[]]::new(10)

Генерирование массивов

Вложенность

Массив внутри других массивов называются вложенными. Работа с ними отличается только индексами:

$array = @(
@(2,2,2),
@(3,3,3)
)

Чтобы получить значение нужно использовать индексы дважды:

$array[0][1]
$array[1][1]

powershell массив строк

Учитывая это можно создать двумерный массив.

 

Двумерные массивы и многомерные

Учитывая предыдущие примеры мы сможем создать двумерный массив или матрицу:

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

Для получения, например, единицы нам нужно использовать индексы дважды:

$dv_massive[0][0]

Если вызвать весь массив, то он будет выведен построчно. Можно вывести и так:

ForEach($el in $dv_massive){Write-Host $el[0] $el[1] $el[2] $el[3]}

powershell двумерный массив

Можно вывести указав SyncRoot:

$dv_massive | select SyncRoot

Многомерный массив можно создать и вывести с параметрами так:

$ma_massive = New-Object 'object[,]' 5,3
# Внесение в первую колонку
$ma_massive[0,0] = 1
$ma_massive[1,0] = 2
$ma_massive[2,0] = 3
$ma_massive[3,0] = 4
$ma_massive[4,0] = 5
# Во вторую
$ma_massive[0,1] = 'A'
$ma_massive[1,1] = 'B'
$ma_massive[2,1] = 'C'
$ma_massive[3,1] = 'D'
$ma_massive[4,1] = 'E'
# Третью
$ma_massive[0,2] = 1988
$ma_massive[1,2] = 1999
$ma_massive[2,2] = 1876
$ma_massive[3,2] = 1478
$ma_massive[4,2] = 1247
# Вывод
Write-Host $ma_massive[0,0] $ma_massive[0,1] $ma_massive[0,2]
Write-Host $ma_massive[1,0] $ma_massive[1,1] $ma_massive[1,2]
Write-Host $ma_massive[2,0] $ma_massive[2,1] $ma_massive[2,2]
Write-Host $ma_massive[3,0] $ma_massive[3,1] $ma_massive[3,2]
Write-Host $ma_massive[4,0] $ma_massive[4,1] $ma_massive[4,2]

powershell двумерный массив 

...

Теги: #powershell


Популярные тэги
Каналы
О блоге
Этот блог представляет собой конспекты выученного материала, преобретенного опыта и лучшие практики в системном администрировании и программировании.