Contents of this directory is archived and no longer updated.

В прошлый раз мы рассмотрели основные вопросы массивов и управления ими в Windows PowerShell. Мы теперь знаем, как они создаются, как изменять их размер (ресайзить) и какие математические операции можно проводить над массивами. В этой части я расскажу о сравнении массивов, поиске элементов и т.д.

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

Как известно, в PowerShell есть куча операторов (или групп операторов) сравнения, как –eq и –like. Но здесь у нас сразу появляется проблема — эти операторы нельзя использовать для сравнения массивов. Давайте посмотрим, что получится:

[↓] [vPodans] "abc" -eq "abc"
True
[↓] [vPodans] 1,2,3 -eq 1,2,3
[↓] [vPodans]

Оператор –eq ничего не вернул. Дело в том, что справа от оператора –eq может быть только один объект, буква, строка, число и т.д. В контексте массивов оператор –eq можно использовать для получения элементов массива, содержащих конкретное значение. Например, у нас есть массив из нескольких чисел и мы хотим узнать сколько раз конкретное число использовано в массиве. Вот простой пример:

[↓] [vPodans] 2,8,5,6,2,5,4,2,7 -eq 2
2
2
2
[↓] [vPodans] 2,8,5,6,2,5,4,2,7 -eq 5
5
5
[↓] [vPodans] 2,8,5,6,2,5,4,2,7 -eq 1
[↓] [vPodans] (2,8,5,6,2,5,4,2,7 -eq 2).Length
3
[↓] [vPodans] (2,8,5,6,2,5,4,2,7 -eq 5).Length
2
[↓] [vPodans] (2,8,5,6,2,5,4,2,7 -eq 1).Length
0
[↓] [vPodans]

Из показанных примеров мы видим, что оператор –eq возвращает элементы, которые соответствуют сравниваемому объекту. Поэтому при помощи оператора –eq можно узнать, содержит ли массив конкретное значение или нет и если да, то сколько раз. То же самое относится и к оператору –like, который больше подходит для сравнения строк по маске:

[↓] [vPodans] "abc","cba","bac","bca" -like "a*"
abc
[↓] [vPodans] "abc","cba","bac","bca" -like "?b?"
abc
cba
[↓] [vPodans]

Здесь используется тот же принцип, что и с использованием оператора –eq, только с разницей, что оператор –like сравнивает по маске (нестрогое соответствие).

Для точного сравнения двух массивов следует использовать командлет Compare-Object:

[↓] [vPodans] Compare-Object 1,2,3 1,2,3
[↓] [vPodans] Compare-Object 1,2,3 1,2

                                                InputObject SideIndicator
                                                ----------- -------------
                                                          3 <=


[↓] [vPodans] Compare-Object 1,2,3 1,2,2

                                                InputObject SideIndicator
                                                ----------- -------------
                                                          2 =>
                                                          3 <=


[↓] [vPodans]

Если массивы одинаковые, командлет ничего не вернёт. Если же есть различия, вы увидите, какие элементы отсутствуют в одном массиве (направление стрелочки указывает от массива с недостающим элментом). Может быть и так, что оба массива одинаковы по размеру, но какие-то элементы имеют разные значения. Тогда вы увидите стрелочки в оба направления. Командлет Compare-Object может выводить и одинаковые элементы:

[↓] [vPodans] Compare-Object -ref 1,2,3 -dif 1,2,2 -IncludeEqual

                                                InputObject SideIndicator
                                                ----------- -------------
                                                          1 ==
                                                          2 ==
                                                          2 =>
                                                          3 <=


[↓] [vPodans]

В одинаковых элементах SideIndicator будет показывать двойной знак равенства (==).

Поиск по массиву

Иногда бывает очень нужным найти индекс элемента массива, значение которого совпадает с какой-то величиной. К сожалению, в PowerShell нет стандартного механизма поиска индексов. Во времена, когда я в повершеле был ещё совсем чайником, Вася Гусев написал мне простенький ванлайнер, который выводит индексы элементов массивов, значения которых совпали по какой-то маске:

function findinarr ($array, $value) {for ($i=0; $i -lt $array.count;$i++){if($array[$i] -eq $value){$i}}}

или более развёрнутый вариант:

function findinarr ($array, $value) {
    for ($i=0; $i -lt $array.count;$i++) {
        if($array[$i] -eq $value){$i}
    }
}

Например, узнать, индекс (или индексы) элемента массива, который содержит цифру 2 и 5:

[↓] [vPodans] $a = 2,8,5,6,2,5,4,2,7
[↓] [vPodans] findinarr $a 2
0
4
7
[↓] [vPodans] findinarr $a 5
2
5
[↓] [vPodans]

Мы видим, что цифра 2 в указанном массиве содержится в элементах с индексами 0, 4 и 7, а цифра 5 в элементах с индексами 2 и 5. Не забудьте, что индексы в массивах начинаются с нуля.

Форматирование массивов

PowerShell отображает массивы в столбик, т.е. каждый элемент массива показывается на новой строке. А если вы хотите показать массив так, чтобы все элементы были в строчку, разделённой пробелами? Можно извратиться конструкцией вида:

$a = 1..100
$string = ""
$a | ForEach-Object {$string += "$_" + " "}
[↓] [vPodans] $a = 1..100
[↓] [vPodans] $string = ""
[↓] [vPodans] $a | ForEach-Object {$string += "$_" + " "}
[↓] [vPodans] $string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 8
3 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
[↓] [vPodans]

Но можно сделать ещё круче — заключить переменную с массивом в двойные кавычки:

[↓] [vPodans] $a = 1..100
[↓] [vPodans] "$a"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 8
3 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
[↓] [vPodans]

как видите, результат точно такой же, только кода израсходовано куда меньше.

Преобразование строк в массивы и обратно

Далеко не всегда мы имеем возможность получить готовый массив, наоборот, нам нужно разбить одну строку на массив строк. Для этого можно использовать метод Split() класса System.String или оператор –split:

[↓] [vPodans] "this is a single string".split()
this
is
a
single
string
[↓] [vPodans] -split "this is a single string"
this
is
a
single
string
[↓] [vPodans]

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

[↓] [vPodans] "00-18-DE-54-57-8E" -split "-"
00
18
DE
54
57
8E
[↓] [vPodans]

Причём, обратите внимание, что оператор –split может быть как унарным (когда всё располагается только справа от оператора), так и бинарным (когда исходная строка находится слева от оператора, остальное располагается справа). Более подробно про оператор –split лучше всего прочитать в справке: http://technet.microsoft.com/en-us/library/dd347708.aspx. Функционал оператора достаточно интересен, но выходит за рамки рассматриваемой статьи.

Есть ещё один трюк, как разделить строку на массив символов. Для этого используется метод ToCharArray():

[↓] [vPodans] "00-18-DE-54-57-8E".tochararray()
0
0
-
1
8
-
D
E
-
5
4
-
5
7
-
8
E
[↓] [vPodans]

Есть ещё один трюк, как разбить строку на массив символов. Для этого надо строку привести к массиву символов (char[]) и результат будет точно такой же:

[char[]]"00-18-DE-54-57-8E"

Если у вас есть массив и его нужно преобразовать в одну строку, можно воспользоваться статическим методом Join() класса System.String или оператором –join:

[↓] [vPodans] $a = 1..10
[↓] [vPodans] $a
1
2
3
4
5
6
7
8
9
10
[↓] [vPodans] [string]::Join(",",$a)
1,2,3,4,5,6,7,8,9,10
[↓] [vPodans] $a -join ","
1,2,3,4,5,6,7,8,9,10
[↓] [vPodans] -join $a
12345678910
[↓] [vPodans]

Как видно из примера, оператор –join так же, как и –split бывает унарным и бинарным. Бинарная форма всегда используется, когда нужно явно указать разделитель или другие параметры оператора –join.  Если оператор унарный, он не принимает явный разделитель и просто последовательно пристыковывает элементы массива в строку. Если вам нужно преобразовать несколько массивов в строки с использованием одного и того же разделителя, в PowerShell можно использовать специальную переменную $ofs и массив явно привести к типу string:

[↓] [vPodans] $ofs = "+"
[↓] [vPodans] [string]$a
1+2+3+4+5+6+7+8+9+10
[↓] [vPodans] iex ([string]$a)
55
[↓] [vPodans]

Как можно видеть из примеров, с массивами в PowerShell можно делать что угодно (кроме вычитания и деления). Причём, зачастую, существует несколько способов выполнить одну и ту же задачу.

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

Вообще это используется не так часто, что можно было бы и опустить, но я достаточно часто работаю с CryptoAPI и бывает нужным первернуть массив. Дело в том, что CryptoAPI до мозга костей little-endian, а остальные API (даже тот же .NET) как правило big-endian. И чтобы перевернуть массив верх тормашками можно использовать статический метод Reverse() класса System.Array:

[↓] [vPodans] $a = 1..5
[↓] [vPodans] $a
1
2
3
4
5
[↓] [vPodans] [array]::Reverse($a)
[↓] [vPodans] $a
5
4
3
2
1
[↓] [vPodans]

Следует учитывать, что этот метод меняет порядок следования элементов в самой перменной и не образует выходной информации.

Эпилог

В качестве эпилога скажу, что я рассмотрел лишь самые популярные действия с массивами и это составляет лишь малую часть того, что с ними можно сделать в реальности. Но этого материала (включая предыдущую статью) вам хватит на 95% случаев. И на этом всё.


Share this article:

Comments:

Stres

Поиск индекса в массиве с помощью класса System.Array .NET: http://blogs.technet.com/b/heyscriptingguy/archive/2011/12/07/find-the-index-number-of-a-value-in-a-powershell-array.aspx Может кому пригодится.

Comments are closed.