Массивы в Powershell являются аналогами списков в Python. Легче всего воспринимать эти понятия как хранение в одной переменной множества значений. То есть это структура данных, которую можно перебирать и вызывать отдельный элемент используя индексы. Массивы, они же и множества и array, в Powershell и других языках программирования являются отдельным типом данных так же как числа и строки.
Создание
Массивы являются базовой возможностью Powershell, и их создание делается просто. Для создания нужно использовать символы @() :
$array = @()
$array.count
Count (в переводе счетчик) - показывает сколько у нас элементов в массиве. Так как мы создали пустое множество он равен нулю.
Можно создать сразу со значениями. Что бы это выполнить нужно добавить значения, разделенные запятой, внутри @() :
$lst = @('Первый', 'Второй', 'Четвертый', 'Третий')
$lst.Count
$lst
Написав $array мы вывели значения переменной. Каждое из четырех значений выводится с новой строчки.
Другой способ создания - это писать значения с новой строчки. В этом случае запятые ставить не обязательно:
$lst = @('Первый'
'2'
'Третий'
4)
$lst.Count
$lst
Хоть такая возможность и есть в Powershell, я бы не советовал использовать без запятых. В остальном, с использованием табуляции, такой синтаксис может улучшить читаемость (особенно если у нас сотни значений).
Еще один способ объявления - это не указывать скобки и знак @ :
$lst = '7',8,'Один','One'
Следующий вариант не используется на практике, но вы можете иметь его ввиду:
$lst = Write-Output Один 2 Трии '4'
Обратите внимание, что число 4 стало строкой только тогда, когда ее поместили в кавычки.
Получение элементов
Как уже было показано выше получить весь массив можно написав переменную, но так же можно использовать другие методы как смещение и индексы.
Индексы
Когда нужно получить конкретный элемент мы указываем скобки []. Самое первое значение массива имеет идентификатор 0. На следующем примере мы получим первый объект:
$arraylist = 'Первый','Второй','Третий'
$arraylist[0]
Для получения следующих индексов просто измените число:
$arraylist[2]
На примерах выше мы получили данные по их индексам. В отличие от большинства языков в Powershell мы можем указывать несколько индексов, которые хотим получить. Так же можно вызвать один и тот же элемент неограниченное количество раз:
$arraylist[0,2]
Срез (slice) или последовательность - это когда мы получаем данные с одного индекса по другой. Последовательности обозначаются двумя точками '..' . Для примера получим первые три значения:
$arraylist = @('admin@login', 'admin', 'Password', '20.02.2004', '????', '????')
$arraylist[0..2]
$arraylist[1..100]
Как видно мы можем получить данные не в строгой последовательности относительно массива. Так же указав несуществующий индекс '100' мы не получили ошибку, а вывели все значения с первого до последнего. Если вызвать единственный элемент, который не будет существовать, то значения будут равны Null.
$null -eq $arraylist[200]
В большинстве языков отрицательное число '-1' обозначает последний объект массива. Обратите внимание, что использование [-1..0] и [0..-1] приведет только к получению первого и последнего элемента:
$arraylist[0..-1]
$arraylist[-1..0]
Можно указать -2 для получения предпоследнего объекта и т.д. Последний объект можно получить и таким образом:
$arraylist.GetUpperBound(0)
Если вы попытаетесь получить индекс у несуществующего массива, или с несуществующими значениями, то это может привести к разным последствиям:
$arraylist = $null,$null
$arraylist[0]
$arraylist = $null
$arraylist[0]
Возможные ошибки:
- Не удается индексировать в массив 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!'}
Используя счетчик можно получить и элемент массива. Если вы никогда не работали с индексами обращайте внимание, что счетчик возвращает количество элементов в массиве, но индексация начинается с 0 элемента. То есть для получения последнего элемента нам нужно вычесть 1:
$lst[$lst.count - 1]
$lst[0..($lst.count - 1)]
$lst[2,($lst.count - 1)]
Если вы не закроете в скобки выражение в случае получения среза, то будут следующие ошибки:
- Сбой вызова метода из-за отсутствия в [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
Указание несуществующего индекса вызовет ошибки:
- Index was outside the bounds of the array
- Индекс находился вне границ массива.
Итерации
Кроме индексов с массивом часто приходится работать используя циклы и другие методы перечисления. Далее будет рассказано об этом.
Конвейер или Pipeline
В примере ниже мы проверим доступность хостов передав каждый объект массива через конвейер:
$sites = 'localhost','fixmypc.ru'
$sites | ForEach-Object {ping $_}
Цикл ForEach
В циклах массивы используются похоже:
foreach ($site in $sites)
{
ping $site
}
Так же доступен метод 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 {'Ничего не создавать'}
}
Обновление значений через циклы
Если нам понадобится обновить весь массив мы обратимся к циклам и мы можем столкнуться с трудностями. Проблемы заключаются в том, что в цикле находится не сам массив, а его значения и мы не сможем их изменить так. Обойти эту ситуацию можно двумя путями.
Первый способ - это обратиться к каждому значению по индексу, а саму 'длину' индексов измерить методами, которые описаны выше:
$val = @(1,2,3)
0..($val.Count-1) | foreach {$val[$_]=$val[$_] + 1}
В 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]
}
Get-Date - примеры работы с датой в 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
Вызвав переменную вы увидите, что представление данных почти не отличается от вывода командлетов.
Для получения данных мы можем указывать индексы:
$data[0]
После получения объекта вызвать свойство ('колонку' с примера выше), которое нам нужно:
$data[0].Computer
$data[0].Process
Когда нужно будет получить, например, все имена компьютеров используем циклы:
$data | ForEach-Object {$_.Computer}
Можно вызвать напрямую или командлет Select-Object:
$data | select Computer
$data.Computer
Фильтрация массивов через 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'
Аналогично способу выше есть метод, который фильтрует так же:
$list.Where({$_.Name -eq 'Dima'})
Обновление объектов через цикл
При использовании циклов таким способом мы будем обновлять значение Name во всех значениях массива:
foreach($iden in $list)
{
$iden.Name = 'Sasha'
}
Если вы хотите заменить конкретное свойство, нужно использовать условия:
$list = @(
[pscustomobject]@{Name='Dima';SubName='Penkin'},
[pscustomobject]@{Name='Valera';SubName='Lerov'}
)
foreach($iden in $list){
if ($iden.Name -eq 'Dima'){$iden.Name='dd'}
}
$list
Заменить весь объект используя цикл нельзя.
Операторы
-join
Join это оператор, который может преобразовать весь массив в единую строку где разделителем может быть любой символ:
$string_array = @('Masha', 'Sasha', 'Dima')
$string_array -join '.'
$string_array -join ' пошел к '
Следующие варианты просто сольют все символы в одну строку без пробелов:
$string_array = @('Masha', 'Sasha', 'Dima')
$string_array -join $null
-join $string_array
Можно использовать для логирования ошибок:
$data = @((Get-Date).Day, 'Error 5051', (hostname))
"Ошибка $data"
-replace
Операторы замены проверит каждый элемент и выполнят замену совпадающих значений:
$massive = 'Lena','Misha','Pika'
$massive -replace 'M','S'
$massive.Replace('L','p')
Более подробно мы уже говорили о replace с Powershell.
-split
Split преобразует строку в список. В качестве разделителя я указал пробел:
$massive = 'Преобразование строки'
$massive -split ' '
$massive.Split(' ')
-contains
Этот оператор проверяет точное вхождение и возвращает булево значение True или False:
$list = 'Вася','Федя'
# Верно
$list -contains 'Вася'
# Не верно
'Вася' -contains $list
-in
Отличия от contains в том, что он появился в Powershell 3 и создан для более удобной читаемости. В операторах Powershell важно соблюдать позиции того что мы ищем и где. Ошибки могут быть очень неявными. В случае с contains объект должен находиться слева, а в in справа:
$list = 'Вася','Федя'
# Верно
'Вася' -in $list
# Не верно
$list -in 'Вася'
Этот оператор так же можно использовать для поиска в массиве Powershell.
-eq и -ne
При использовании сравнения 'eq' у нас вернется либо само значение, либо False. В случае неравенства 'ne' вернутся либо остальные значения, либо True. Важно использовать искомое значение справа, а объект слева:
$data = @('один','два','три')
# Верно
$data -eq 'один'
$data -ne 'один'
# Не верно
'один' -eq $data
'один' -ne $data
Если нужно проверить, что все значения в листах совпадают требованиям, можно воспользоваться такой конструкцией:
$array = @('Ok','Ok','not Ok')
if ( -not ( $array -ne 'Ok') ){'Что-то не так, это False'}
$array = @('Ok','Ok','Ok')
if ( -not ( $array -ne 'Ok') ){'Все ок'}
Оператор match позволяет использовать регулярные выражения. Он будет искать каждое совпадение в массиве и вернет их:
$var = @('QQ-11-22','22-44-SS','FF-GG-HH')
# Верно
$var -match '22'
# Не верно
'22' -match $var
В операторе like можно использовать знак *, который будет идентифицировать пропущенный текст. Следующий пример сработает с ошибкой в случае с match:
$var = @('QQ-11-22','22-44-SS','FF-GG-HH')
# Верно
$var -like '2*S'
# Не верно
'2*S' -like $var
Похоже сработает 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 используется командлет 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
Создание из конвейера
Можно генерировать новый массив используя старый:
$array_add = @(0)
1..5 | ForEach-Object { $array_add+=$_ }
$array_add
Типы массивов
По умолчанию массив в 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
Универсальные списки
Универсальные списки относятся к 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)
Следующий синтаксис добавит число в список:
$list.Add(10)
Значения так же можно получать по индексам:
$list[-1]
Для удаления используется следующий синтаксис:
using namespace System.Collections.Generic
$var = [List[string]]@('Один','Два','Три')
[void]$var.Remove('Один')
Можно так же удалять по индексам:
$delete = $var[1]
$var.remove($delete)
List[PSObject]
Список из любого типа данных можно создать и так:
using namespace System.Collections.Generic
$list = [List[PSObject]]::new()
ArrayList
Для поддержки удаления, которого нет в обычных массивах, можно использовать ArrayList. Они так же быстрее работают. Для создания такого массива используйте следующий синтаксис:
$arraylist = [System.Collections.ArrayList]::new()
[void]$arraylist.Add('Someval')
$arraylist
Для удаления:
$arraylist.Remove('Someval')
Другие методы по работе с массивами
Кроме основных методов по работе с массивами есть методы, которые позволяют генерировать списки и создавать фиксированного размера. Далее разберем на примерах.
Фиксированный размер
В Powershell можно создать список с предопределенным размером:
$data = [Object[]]::new(5)
$data.count
Умножение
Так же как и в других языках массивы можно умножать:
$var = @('A','B','C')
$var*5
Похожий метод, который создаст 10 нулей:
[int[]]::new(10)
Вложенность
Массив внутри других массивов называются вложенными. Работа с ними отличается только индексами:
$array = @(
@(2,2,2),
@(3,3,3)
)
Чтобы получить значение нужно использовать индексы дважды:
$array[0][1]
$array[1][1]
Учитывая это можно создать двумерный массив.
Двумерные массивы и многомерные
Учитывая предыдущие примеры мы сможем создать двумерный массив или матрицу:
$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]}
Можно вывести указав 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]
...
Подписывайтесь на наш Telegram канал
Теги: #powershell