Особенности работы с массивами в PowerShell. Powershell работа с массивами


Особенности работы с массивами в PowerShell

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

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

$a = 1, 2, 3 $a.GetType()

Как видите, в данном случае мы получаем массив (System.Array). Но если в переменную поместить одно значение, то PowerShell не будет формировать массив:

$b = 1 $a.GetType()

 

Как видно из примера, PowerShell определяет тип данных в переменной в зависимости от количества объектов. Но мы не всегда можем точно заранее сказать, сколько объектов будет получено в результате выполнения той или иной команды, поэтому необходимо иметь код, который будет всегда рассматривать результат как массив.

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

$b = ,1

 

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

[object[]]$b = 1

 

Здесь надо упомянуть, что каждый элемент массива также имеет свой тип данных, а тип object[] позволяет добавлять в массив любые объекты. При необходимости можно ограничить члены массива определенным типом данных (типизированный массив). Например можно указать в качестве элементов массива только целочисленные значения:

[int32[]]$b = 1

А так мы определим в качестве членов массива системные процессы:

[System.Diagnostics.Process[]]$b = Get-Process

 

Также создать массив можно с помощью оператора подвыражения массива ″@″, который формирует массив в любом случае, даже при полном отсутствии объектов. Очень удобно сначала инициализировать переменную как массив, а затем добавлять в нее нужные объекты, не беспокоясь об их количестве:

$b = @()$b = 1

 

Ну и в завершение еще один способ, подсмотренный на MSDN. Его суть в том, что объекты передаются по конвейеру специальной функции, которая преобразует их в массив. Выглядит примерно так:

function Out-Array {begin {$out = @()}; process {$out += $_}; end {return, $out}} $b = 1 | Out-Array

 

Способ не самый прямой и не самый быстрый, но вполне рабочий.

windowsnotes.ru

PowerShell для программиста. Массив массивов в PowerShell

Уже несколько раз столкнулся с особенностями создания различных иерархий посредством массивов и хешей. И если с ассоциативными массивами (хэшами) проблем как правило нет, то вот с обычными уже как карта ляжет. Проблема в том, что PowerShell пытается додумывать и незримо конвертировать типы. Решил увековечить в общем.

Как думаете, что будет результатом выражения:$a = @(1,2,3,@(4,5,6))

Правильно! Одномерный массив @(1,2,3,4,5,6)

А вот что бы не произошло объединения массивов, а все таки 4-й элемент стал именно вложенным массивом надо добавить магическую запятую:$a = @(1,2,3, , @(4,5,6))

Собсно, везде где мы хотим что бы массив остался массивом, самый простой вариант это добавить запятую впереди. Можно конечно работать с каким-нибудь C# массивом/коллекцией (соответствующим образом объявив) и там проблем не будет.

Ниже полный код скрипта где реализованы примеры вложенных массивов и хэшей на примере простейшей рекурсивной функции. Для наглядности результата сделал преобразование в JSON.Конструкции -replace “\s”,”” – реализуют минификацию, можно убрать – будет развернутый JSON

clear function RecursionHash($deep) { $la = @{} $la["item$deep"] = "Iteration $deep" if ($deep -gt 0) { $la["sub"] = RecursionHash -deep ($deep-1) } return $la } function RecursionArray($deep) { $la = @() if ($deep -gt 0) { $la += ,@((RecursionArray -deep ($deep-1)), "Iteration $deep") # comma is a magic } return $la } $hash = RecursionHash -deep 3 (ConvertTo-Json $hash) -replace "\s","" $arr = RecursionArray -deep 3 (ConvertTo-Json $arr) -replace "\s",""

Много магии вот в этой строке:$la += ,@((RecursionArray -deep ($deep-1)), “Iteration $deep”)

Собака нужна, как явное объявление и инициализация типа, иначе самый глубокий элемент будет не понять чем (в данном случае пустой хэш почему-то):$la += ,((RecursionArray -deep ($deep-1)),$deep)[[[[[{},1],2],3],4],5]

А перед ней запятая, что бы последний (глубокий) элемент именно вложился в предпоследний, а не добавился к предпоследнему массиву:$la += @((RecursionArray -deep ($deep-1)),$deep)[[[[1,2],3],4],5](собаку можно было и не ставить, тут даже PoSh-у понятно ч

vms11.wordpress.com

Массивы в Windows PowerShell, часть 2

Продолжаем разговор о массивах в Windows PowerShell. В предыдущей части мы говорили об обращении к элементам массива. Сегодня поговорим об операциях с массивом.

Операции с массивом

По умолчанию все массивы в PowerShell являются полиморфными, т.е. способны содержать в себе элементы разных типов.

Давайте создадим массив $a, состоящий, к примеру, из трех элементов 1, 2 и 3. И проверим его тип на практике.

Итак, как видим, для того, чтобы посмотреть тип массива $a, нам нужно набрать

$a.GetType().FullName

и мы увидим, что это массив типа System.Object[]. Такие массивы могут содержать объекты всех типов.

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

[int[]]$a=1,2,3

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

Теперь поговорим о том, как можно изменять уже существующие массивы.

К примеру, наш массив $a имеет три элемента, если мы попытаемся обратиться к элементу, выходящему за границы массива, PowerShell просто проигнорирует нас. А вот если мы попытаемся изменить значение несуществующего элемента, нас встретит сообщение об ошибке.

Если нам нужно увеличить длину массива, мы можем использовать операторы + или +=.

По факту, в данном случае мы не добавили элементы к существующему массиву, а создали новый, в который было скопировано содержимое старого массива и добавлены новые элементы.

По той же логике мы не можем удалить элементы из уже существующего массива, но мы можем создать новый массив и скопировать туда все элементы, кроме ненужных.

Объединить массивы довольно просто.

Для удаления массива нужно воспользоваться командлетом Remove-Item  (псевдоним del) и удалить массив с виртуального диска variable. Ниже пример удаления массива $a.

И последнее, что нужно знать о массивах. Если мы просто используем оператор присваивания (=), то новый массив будет своеобразной ссылкой на предыдущий. К примеру, $b=$a создаёт массив $b, который ссылается на $a. Если мы изменим значения элементов массива $a, то они изменятся и в $b. Данное правило не касается массивов, полученных из объединения других.

webistore.ru

Массивы в Powershell: yu_xuan

При написании скриптов массивы используются очень часто, поэтому необходимо хорошо понимать как работать с ними.

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

PS C:\> 1,2,3,4 1 2 3 4PS C:\> (1,2,3,4).GetType().FullName System.Object[]

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

PS C:\> 1, 2.5, "apples", (Get-Process winword) 1 2,5 apples Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 1333 86 47844 66728 350 581,15 10448 WINWORDPS C:\> (1, 2.5, "apples", (Get-Process winword)).Count 4

В предыдущем примере создан массив объектов, содержащий число, число двойной точности с плавающей запятой, строку и объект Process, т.е. всего четыре объекта: int, double, string, System.Diagnostics.Process instance.

Для создания массива используется довольно свободный синтаксис, однако существует формальный способ, заключающийся в использовании конструкции @() :

PS C:\> @("one", "two") one two

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

PS C:\> (@()).Count 0PS C:\> (@()).GetType().FullName System.Object[]

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

PS C:\> 5..1 5 4 3 2 1PS C:\> 1..5 1 2 3 4 5PS C:\> (0..255).Count 256

Такой синтаксис чрезвычайно удобно использовать для перечисления большого набора упорядоченных чисел таких как номера портов или адреса.

При создании массивов powershell умеет распознавать вложенные массивы и превращать их в один массив:

PS C:\> 1, (5, 6), 2 1 5 6 2PS C:\> 1, (10..7), 2 1 10 9 8 7 2

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

PS C:\> $a = 2,3,4 PS C:\> $a[0] 2 PS C:\> $a[1] 3 PS C:\> $a[2] = 5 PS C:\> $a 2 3 5

Заметьте, что мы изменили член массива, присвоив ему новое значение. Массивы powershell, также как .Net, начинаются с нуля, т.е. первый член массива всегда имеет индекс [0], а последний член [-1].Интересно, что в квадратных скобках можно указать больше одного индекса. В таком случае мы получим массив, содержащий значения этих индексов. Такой массив называется срез массива (array slice). Вот как можно получить значения первого и последнего членов:

PS C:\> $a = 2,3,4 PS C:\> $a[0,2] 2 4

Предыдущий пример неудобен в использовании, поскольку мы должны точно знать, что массив состоит из трех членов (именно поэтому для последнего члена был указан индекс 2). Многие языки программирования, включая powershell, позволяют обращаться к членам массива, используя отрицательные индексы. Вот, как можно изменить предыдущий пример, для получения первого и последнего члена, не зная точное количество членов:

PS C:\> $a = 2,3,4 PS C:\> $a[0,-1] 2 4

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

PS C:\> $a = (1, 2, 3, 4, 5, 6, 7, 8) PS C:\> $a[2..6] 3 4 5 6 7

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

PS C:\> $a = 1,2,3 PS C:\> $a[1] = 10,11,12 PS C:\> $a 1 10 11 12 3

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

PS C:\> $a = 1,2,3 PS C:\> $a[1] = $a[1], 10, 11, 12 PS C:\> $a 1 2 10 11 12 3

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

PS C:\> $a = 1,2 PS C:\> $a = $a + 3 PS C:\> $a 1 2 3

Предыдущуюю операцию можно немного упростить при помощи оператора += :

PS C:\> $a += 4 PS C:\> $a 1 2 3 4PS C:\> $a += 5,6 PS C:\> $a 1 2 3 4 5 6

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

PS C:\> foreach ($item in (2,3,4)){if ($item -eq 3){echo "Found"}} Found

Конечно, такой способ весьма неповоротлив, так как приходится перебирать в цикле весь массив, сравнивая по очереди каждое значение. Для облегчения этой муторной операции в powershell существуют операторы -contains и -notcontains:

PS C:\> (2,3,4) -contains 3 True PS C:\> (2,3,4) -notcontains 3 False PS C:\> (2,3,4) -notcontains 5 True

Эти операторы можно использовать для работы с переменными, содержащими различные типы данных:

PS C:\> (2, "some value", 4) -contains "some value" True

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

PS C:\> (2,3,4) -contains "3" True PS C:\> (2,3,4) -contains "3.0" True

Понимаете, каким образом строка "3.0" превращается в число 3 и говорит True? Для того, чтобы жестко указать тип данных можно использовать команду Where-Object:

PS C:\> (2,3,4) | Where-Object {$_ -is [string] -and $_ -eq "3.0"} PS C:\> (2,"3.0",4) | Where-Object {$_ -is [string] -and $_ -eq "3.0"} 3.0

слегка вольный перевод главы из книги "Pro Windows PowerShell"

yu-xuan.livejournal.com

Основы PowerShell: массивы и таблицы хеширования | Windows IT Pro/RE

Массивы и таблицы хеширования – две наиболее важных структуры данных, доступные в современных языках написания сценариев. Оболочка Windows PowerShell (http://www.windowsitpro.com/topics/powershell-scripting) поддерживает обе эти структуры. Массив, который иногда называют «коллекцией», содержит список элементов. Таблица хеширования, которую еще называют «словарем» или «массивом ассоциаций», содержит списки пар элементов. Далее в статье я покажу, как создавать массивы и таблицы хеширования, а также получать доступ к их элементам.

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

Массивы полезны в тех случаях, когда вы собираетесь хранить и просматривать список или коллекцию элементов. Один из простейших способов создать массив – использовать команду PowerShell, которая выводит более одного объекта. Например, в результате простого запуска команды Get-ChildItem будет выведен список объектов в текущем расположении. Этот список может быть сохранен в переменной. Если текущее расположение содержит более одного объекта, переменная, в которой вы сохранили список, становится массивом. Это действительно просто. Например, рассмотрим следующую команду PowerShell:

$list = Get-ChildItem $ENV:SystemRoot

После выполнения этой команды переменная $list содержит массив объектов FileInfo и DirectoryInfo.

Другой способ создать массив – указать объекты, разделив их запятыми, и присвоить их переменной:

$list = «A",»B«,"C»,«1",»2«,"3»

Доступ к элементам массива

Существует три способа обращения к элементам массива.

  • Использование инструкции foreach. Инструкция foreach оболочки PowerShell предоставляет удобный способ обращения к любому элементу массива. Например, чтобы вывести на экран элемент массива $list, нужно запустить команду:
foreach ( $item in $list ) { $item }
  • Использование команды ForEach-Object. Команда ForEach-Object рассматривает массив как входные данные из конвейера и передает каждый объект массива в блок сценария для обработки. Текущий элемент массива представлен специальной переменной $_. Чтобы вывести на экран элемент массива $list с помощью команды ForEach-Object, используйте команду:
$list | ForEach-Object { $_ }
  • Применение инструкции с номером индекса. К каждому объекту (элементу) массива можно обратиться по соответствующему номеру, начиная с 0. Например, выражение $list[0] ссылается на первый элемент в массиве, выражение $list[1] ссылается на второй элемент, и так далее. Так, чтобы вывести на экран каждый член массива с помощью инструкции for и номера индекса, запустите команду:
for ( $i = 0; $i -lt $list.Count; $i++ ) { $list[$i] }

Из этих трех подходов способ с индексами используется реже всего. Основная причина в том, что инструкция foreach и команда ForEach-Object обеспечивают более быстрое выполнение задачи.

При изучении этих трех подходов основной вопрос звучит так: в чем разница между использованием инструкции foreach и команды ForEach-Object? Разница в первую очередь в том, что при использовании инструкции foreach необходимо извлечь все элементы массива, прежде чем вы сможете получить доступ к ним. И наоборот, команда ForEach-Object использует преимущества конвейера PowerShell, позволяя получить доступ сразу к отдельному элементу.

Какой же из подходов выбрать? Ответ зависит от элементов массива. Если у вас сравнительно небольшое количество элементов массива и извлечение не займет много времени, инструкция foreach будет работать отлично. Если же у вас большой список элементов и извлечение элементов требует значительного времени (например, файлы на удаленном файловом сервере), вы, скорее всего, захотите использовать команду ForEach-Object. Также стоит подумать о применении команды Write-Progress. Если вы хотите задействовать эту команду в сценарии, чтобы информировать пользователя о ходе выполнения операции, вам придется предварительно извлечь все объекты с помощью инструкции foreach – это необходимо для расчета процента выполненной работы.

Есть еще один момент, из-за которого могут возникнуть затруднения при выборе между инструкцией foreach и командой ForEach-Object: псевдонимом команды ForEach-Object является ключевое слово foreach! Не забывайте, что при использовании выражения foreach после вертикальной черты (|) в конвейере PowerSh ell на самом деле вы вызываете команду ForEach-Object.

Другое затруднение, связанное с массивами, может проявиться, если вы знакомы с языками VBScript или JavaScript. В этих двух языках написания сценариев массивы и коллекции являются разными типами объектов. Однако в оболочке PowerShell вы можете рассматривать их как одно и то же.

Создание таблицы хеширования

Таблицы хеширования полезны в том случае, когда вам нужно хранить и извлекать объекты, обращаясь к ним по именам. Например, вы можете использовать таблицу хеширования, чтобы хранить список серверов и соответствующих им IP-адресов.

Вы можете создать таблицу хеширования путем указания имени и значения между символами @{ и }. Например, чтобы создать таблицу хеширования, которая содержит имена трех серверов и их IP-адреса, следует выполнить команду:

$hash = @{«server1» = «192.168.17.21»; «server2» = «192.168.17.22»; «server3» = «192.168.17.23»}

Если вывести эту таблицу хеширования на экран (просто написать $hash в строке PowerShell и нажать клавишу ввода), вы увидите результаты, приведенные на рисунке. Заметьте, что оболочка PowerShell выводит данные столбца Name в произвольном порядке. Одна из особенностей таблицы хеширования заключается в том, что именованные элементы не имеют определенного порядка.

 

Рисунок. Таблица хэширования

Доступ к элементам таблицы хеширования

Чтобы получить значение из таблицы хеширования, вы можете указать имя таблицы хеширования и далее имя элемента, значение которого вы хотите получить, заключенное в квадратные скобки. Либо вы можете поставить точку между именем таблицы и именем элемента, значение которого вы хотите получить. Например, для созданной ранее переменной $hash любая из приведенных ниже команд вернет значение 192.168.17.21:

$hash[«server1»] $hash.server1 $hash.«server1»

Имейте в виду, что кавычки в команде $hash[«server1»] необходимы. Без них оболочка PowerShell не сможет получить информацию о том, каким образом обрабатывать ключ server1. Однако если имя сервера хранится в переменной (например, $serverName), вы можете применить команду:

$hash[$serverName]

Некоторые команды PowerShell используют таблицы хеширования в качестве значений для параметров. Например, команда Select-Object задействует таблицы хеширования для вывода на экран вычисляемых свойств. Чтобы использовать таблицу хеширования при создании вычисляемого свойства для команды Select-Object, она должна иметь два парных значения: Name, которое задает имя вычисляемого свойства, и Expression, которое содержит блок сценариев, отвечающий за вычисление свойства. Например, возьмем команду:

Get-ChildItem $ENV:SystemRoot | select-object Name, LastWriteTime, @{Name=«Day»; Expression={$_.LastWriteTime.DayOfWeek}}

В этом случае команда Select-Object выведет имя, время последней записи и день недели для каждого файла и каталога в установочной папке Windows. Как и в случае с командой ForEach-Object, специальная переменная $_ ссылается на текущий объект.

.

www.osp.ru

Ассоциативные массивы в Powershell: yu_xuan

Ассоциативные массивы - это массивы, содержашие набор пар ключ-значение, позволяющие легко и удобно отыскивать значение по ключу.Синтаксис создания ассоциативного массива в powershell подобен созданию обычных массивов; разница состоит лишь в использовании скобок (для обычных массивов используются круглые скобки, а для ассоциативных - фигурные).

Для примера, рассмотрим создание ассоциативного массива, содержащего сведения об имени "Name" и адресе "Address" какого-то человека:

PS C:\> $d = @{"Name"="John"; "Address"="12 Easy St."} PS C:\> $d Name Value ---- ----- Name John Address 12 Easy St.

Для создания ассоциативного массива powershell использует объект System.Collections.Hashtable:

PS C:\> $d.GetType().FullName System.Collections.Hashtable

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

PS C:\> $d = @{"Name"="John"; "Age"=30; ` >> "File"=(Get-Item C:\tmp\test.txt)} >> PS C:\> $d Name Value ---- ----- Name John Age 30 File C:\tmp\test.txt

Как видно в предыдущем примере, в массиве $d хранится строка, int и объект FileInfo.

Для ключей также можно использовать любой тип данных. Например, у нас есть два работающих процесса MS Word и MS Outlook, мы можем объекту Process присвоить какое-то строковое значение:

PS C:\> $set = @{(Get-Process winword)="MS Word"; (Get-Process outlook)="MS Outlook"} PS C:\> $set Name Value ---- ----- System.Diagnostics.Process ... MS Outlook System.Diagnostics.Process ... MS Word

Можно создать пустой ассоциативный массив при помощи конструкции @{} :

PS C:\> $empty = @{} PS C:\> $empty.GetType().FullName System.Collections.Hashtable

Получить доступ к данным возможно несколькими способами. Основной - это доступ при помощи квадратных скобок, как и к обычному массиву:

PS C:\> $d = @{"Name"="John"; "Age"=30; "Address"="12 Easy St."} PS C:\> $d["Name"] John

Ассоциативные массивы могут вести себя как объекты, то есть мы можем получить доступ к значениям при помощи точечной нотации:

PS C:\> $d = @{"Name"="John"; "Age"=30; "Address"="12 Easy St."} PS C:\> $d.Name John

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

PS C:\> $d = @{"Person Name"="John"; "Age"=30; "Address"="12 Easy St."} PS C:\> $d."Person Name" John

В качестве ключа никто не запрещает использовать переменную:

PS C:\> $d = @{"Name"="John"; "Age"=30; "Address"="12 Easy St."} PS C:\> $property = "Name" PS C:\> $d.$property John

и выражения:

PS C:\> $d.$($property) John PS C:\> $d.$("Na"+"me") John

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

PS C:\> $d = @{"Name"="John"; "Age"=30; "Address"="12 Easy St."} PS C:\> $d["Name", "Address"] John 12 Easy St.

Добавление членов в ассоциативный массив возможно либо при помощи точки, либо - квадратных скобок:

PS C:\> $d.Department = "Accounting" PS C:\> $d["SSN"] = 123456789 PS C:\> $d Name Value ---- ----- Department Accounting Name John Age 30 SSN 123456789 Address 12 Easy St.

Для удаления используется метод Remove() с указанием ключа:

PS C:\> $d.Remove("Age") PS C:\> $d.Remove("SSN") PS C:\> $d Name Value ---- ----- Department Accounting Name John Address 12 Easy St.

Другие важные методы ассоциативных массивов, которые стоит запомнить, - это Contains() и ContainsKey(). По сути они являются синонимами и позволяют проверить массив на присутствие какого-то ключа:

PS C:\> $d.Contains("John") False PS C:\> $d.Contains("Name") True PS C:\> $d.ContainsKey("Name") True

Метод ContainsValue() позволяет определить, существует ли искомое значение в массиве:

PS C:\> $d.ContainsValue("John") True PS C:\> $d.ContainsValue("Name") False

В дополнение к этому существуют свойства массивов, позволяющие отобразить все ключи и все значения, это соответственно Keys и Values:

PS C:\> $d.Keys Department Name Address PS C:\> $d.Values Accounting John 12 Easy St.

Можно заменить методы ContainsKey() и ContainsValue() поиском по массиву:

PS C:\> $d.Keys -contains "Name" True PS C:\> $d.Values -contains "John" True

Не забывайте, что -contains тихой сапой конвертирует типы данных, а методы ContainsKey() и ContainsValue() - не конвертируют.

Ну, и на последок можно упомянуть о том, что свойство Count может показать нам количество членов массива:

PS C:\> $d = @{"Name"="John"; "Age"=30; "Address"="12 Easy St."} PS C:\> $d.Count 3

слегка вольный перевод главы из книги "Pro Windows PowerShell"

yu-xuan.livejournal.com

Как отсортировать массив в PowerShell

Довольно часто для хранения данных в PowerShell используются одномерные массивы вида ″ключ=значение″ (name=value). И наиболее распространенным типом массива этого вида являются hash-массивы (hashtables).

Напомню, что hash-массив представляет коллекцию пар ключ-значение, которые упорядочены по хэш-коду ключа. В PowerShell для создания hash-массивов существует упрощенный синтаксис. Для примера создадим массив и выведем его содержимое:

$array = @{a=1;b=2;c=3}

 

Как видно из примера, содержимое hash-массива выдается не совсем в том порядке, в котором оно было добавлено. Попробуем изменить порядок, отсортировав вывод с помощью командлета Sort-Object:

$array | Sort-Object -Name

 

Однако номер не прошел и порядок остался прежним. Дело в том, что в данном случае массив передается как единый объект, соответственно командлету Sort-Object сортировать просто нечего. Исправить это можно с помощью метода GetEnumerator, который извлекает каждую строку массива как отдельный объект и передает ее по конвейеру. Например:

$array.GetEnumerator() | Sort-Object Name

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

$array.GetEnumerator() | Sort-Object Name -Descending

 

В принципе вопрос решен, но ради интереса поместим получившийся массив в переменную $newarray и сравним его тип с исходным. Как видите, оба массива принадлежат пространству имен System.Collections, но исходный массив относится к классу Hashtable, а новый — к DictionaryEntry. О различиях между этими классами можно почитать здесь.

Примечание. Если быть точным, то DictionaryEntry является не классом, а структурой.

 

Ну и раз уж речь зашла о классах, проверим, какие еще из них можно использовать для упорядочивания и сортировки массивов. Так класс SortedList представляет из себя коллекцию объектов, отсортированных по имени:

[System.Collections.SortedList]$array = @{a=1;b=2;c=3}

 

Класс Stack  представляет неупорядоченную коллекцию объектов и работает по принципу стека, т.е. последним пришел — первым ушел:

[System.Collections.Stack]$array = @{a=1;b=2;c=3}

А класс Queue наоборот, работает по принципу очереди (первый пришел — первый ушел):

[System.Collections.Queue]$array = @{a=1;b=2;c=3}

 

И еще один класс OrderedDictionary, принадлежащий к пространству имен System.Collections.Specialized. Он также представляет из себя коллекцию пар ″ключ-значение″,  доступ к которым можно получить по ключу или по индексу. Если не вдаваться в подробности, то этот класс представляет из себя что-то вроде хэш-массива, элементы в котором упорядочены по индексу. Класс OrderedDictionary можно  использовать только с хэш-массивами. Он появился в PowerShell 3.0, и для его обозначения существует сокращенный синтаксис. Например:

$array = [ordered]@{a=1;b=2;c=3}

 

Обратите внимание, что название класса ставится не перед именем переменной, а перед знаком @. При попытке указать для переменной данный тип будет выдана ошибка:

[ordered]$array = @{a=1;b=2;c=3}

 

Такую же ошибку мы получим при попытке упорядочить уже созданный хэш-массив.

$array = @{a=1;b=2;c=3}$newarray = [ordered]$array

 

Таким образом, упорядочить массив можно только при его создании, сконвертировать готовый массив не получится. Впрочем при необходимости можно написать несложный скрипт для преобразования, а если лень — то  воспользоваться готовым.  Скрипт для конвертирования ConvertTo-OrderedDictionary можно найти в галерее Technet.

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

windowsnotes.ru