about_Arrays
Краткое описание
Описывает массивы, которые являются структурами данных, предназначенными для хранения коллекций элементов.
Подробное описание
Массив — это структура данных, предназначенная для хранения коллекции элементов. Элементы могут быть одинаковыми или разными типами.
Начиная с Windows PowerShell 3.0 коллекция из нуля или одного объекта имеет некоторые свойства массивов.
Создание и инициализация массива
Чтобы создать и инициализировать массив, назначьте несколько значений переменной. Значения, хранящиеся в массиве, разделены запятыми и отделены от имени переменной оператором назначения (=
).
Например, чтобы создать массив с именем $A
, содержащий семь числовых (целочисленных) значений 22, 5, 10, 8, 12, 9 и 80, введите:
$A = 22,5,10,8,12,9,80
Запятую можно также использовать для инициализации массива отдельных элементов, поместив запятую перед одним элементом.
Например, чтобы создать один массив элементов с именем $B
, содержащим одно значение 7, введите:
$B = ,7
Вы также можете создать и инициализировать массив с помощью оператора диапазона (..
).
В следующем примере создается массив, содержащий значения 5–8.
$C = 5..8
В результате $C
содержит четыре значения: 5, 6, 7 и 8.
Если тип данных не указан, PowerShell создает каждый массив в виде массива объектов (System.Object[]). Чтобы определить тип данных массива, используйте GetType()
этот метод. Например:
$A.GetType()
Чтобы создать строго типизированный массив, то есть массив, который может содержать только значения определенного типа, приведение переменной в качестве типа массива, например string[], long[] или int32[]. Чтобы привести массив, предшествуйте имени переменной с типом массива, заключенным в квадратные скобки. Например:
[int32[]]$ia = 1500, 2230, 3350, 4000
В результате $ia
массив может содержать только целые числа.
Вы можете создать массивы, приводимые к любому поддерживаемому типу в .NET. Например, объекты, извлекаемые Get-Process
для представления процессов, относятся к типу System.Diagnostics.Process . Чтобы создать строго типизированный массив объектов процесса, введите следующую команду:
[Diagnostics.Process[]]$zz = Get-Process
Оператор вложенных выражений массива
Оператор вложенных выражений массива создает массив из инструкций внутри него. Независимо от того, какой оператор внутри оператора создает, оператор помещает его в массив. Даже если имеется ноль или один объект.
Синтаксис оператора массива выглядит следующим образом:
@( ... )
Оператор массива можно использовать для создания массива с нулевым или одним объектом. Например:
$a = @("Hello World")
$a.Count
1
$b = @()
$b.Count
0
Оператор массива полезен в сценариях при получении объектов, но не знаю, сколько ожидается. Например:
$p = @(Get-Process Notepad)
Дополнительные сведения о операторе вложенных выражений массива см. в about_Operators.
Доступ и использование элементов массива
Чтение массива
Вы можете ссылаться на массив, используя его имя переменной. Чтобы отобразить все элементы в массиве, вызовите имя массива. Например, $a
это массив чисел от 0 до 9:
$a
0
1
2
3
4
5
6
7
8
9
Вы можете ссылаться на элементы массива с помощью индекса. Заключите номер индекса в квадратные скобки. Значения индекса начинаются с 0
. Например, чтобы отобразить first элемент в массиве $a
, введите:
$a[0]
0
Чтобы отобразить третий элемент в массиве, введите следующее $a
:
$a[2]
2
Часть массива можно получить с помощью оператора диапазона для индекса. Например, чтобы получить второй до пятых элементов массива, введите следующее:
$a[1..4]
1
2
3
4
Отрицательные count числа из конца массива. Например, -1
ссылается на last элемент массива. Чтобы отобразить last три элемента массива, в порядке возрастания индекса введите:
$a = 0 .. 9
$a[-3..-1]
7
8
9
Если вы вводите отрицательные индексы в порядке убывания, выходные данные изменяются.
$a = 0 .. 9
$a[-1..-3]
9
8
7
Однако будьте осторожны при использовании этой нотации. Нотация циклов от конечной границы до начала массива.
$a = 0 .. 9
$a[2..-2]
2
1
0
9
8
Кроме того, одна из распространенных ошибок заключается в том, чтобы предположить $a[0..-2]
, что относится ко всем элементам массива, кроме last одного. Он относится к firstlastэлементам , а также к элементам второго типаlast в массиве.
Оператор plus (+
) можно использовать для объединения диапазонов со списком элементов в массиве. Например, чтобы отобразить элементы в позициях индекса 0, 2 и 4–6, введите:
$a = 0 .. 9
$a[0,2+4..6]
0
2
4
5
6
Кроме того, для перечисления нескольких диапазонов и отдельных элементов можно использовать оператор plus. Например, чтобы вывести список элементов нулю до двух, четырех до шести, а элемент в восьмом позициальном типе:
$a = 0..9
$a[+0..2+4..6+8]
0
1
2
4
5
6
8
Итерации по элементам массива
Можно также использовать конструкции циклического цикла, такие как foreach
, for
и while
циклы, для ссылки на элементы в массиве. Например, чтобы использовать foreach
цикл для отображения элементов в массиве $a
, введите:
$a = 0..9
foreach ($element in $a) {
$element
}
0
1
2
3
4
5
6
7
8
9
Цикл foreach
выполняет итерацию по массиву и возвращает каждое значение в массиве until , достигающее конца массива.
Цикл for
полезен при добавочных счетчиках при проверке элементов в массиве. Например, чтобы использовать for
цикл для возврата каждого другого значения в массиве, введите:
$a = 0..9
for ($i = 0; $i -le ($a.length - 1); $i += 2) {
$a[$i]
}
0
2
4
6
8
Вы можете использовать while
цикл для отображения элементов в массиве until определенного условия больше не имеет значения true. Например, чтобы отобразить элементы в массиве $a
, а индекс массива меньше 4, введите:
$a = 0..9
$i=0
while($i -lt 4) {
$a[$i]
$i++
}
0
1
2
3
Свойства массивов
Count или Length или LongLength
Чтобы определить, сколько элементов находится в массиве, используйте Length свойство или его Count псевдоним. Longlength полезно, если массив содержит более 2 147 483 647 элементов.
$a = 0..9
$a.Count
$a.Length
10
10
Rank
Возвращает число измерений в массиве. Большинство массивов в PowerShell имеют только одно измерение. Даже если вы считаете, что вы создаете многомерный массив, как показано в следующем примере:
$a = @(
@(0,1),
@("b", "c"),
@(Get-Process)
)
"`$a rank: $($a.Rank)"
"`$a length: $($a.Length)"
"`$a[2] length: $($a[2].Length)"
"Process `$a[2][1]: $($a[2][1].ProcessName)"
В этом примере создается одномерный массив, содержащий другие массивы. Это также называется массивом с замеченным элементом. Свойство Rank доказало, что это одномерное. Чтобы получить доступ к элементам в массиве, индексы должны находиться в отдельных квадратных скобках ([]
).
$a rank: 1
$a length: 3
$a[2] length: 348
Process $a[2][1]: AcroRd32
Многомерные массивы хранятся в основном порядке строк. В следующем примере показано, как создать действительно многомерный массив.
[string[,]]$rank2 = [string[,]]::New(3,2)
$rank2.rank
$rank2.Length
$rank2[0,0] = 'a'
$rank2[0,1] = 'b'
$rank2[1,0] = 'c'
$rank2[1,1] = 'd'
$rank2[2,0] = 'e'
$rank2[2,1] = 'f'
$rank2[1,1]
2
6
d
Чтобы получить доступ к элементам в многомерном массиве, разделите индексы с помощью запятой (,
) в одном наборе квадратных скобок ([]
).
Для некоторых операций с многомерным массивом, таким как репликация и объединение, требуется, чтобы массив был неструктурирован. Неструктурированное преобразование преобразует массив в 1-мерный массив без ограничений типа. Полученный в результате массив принимает все элементы в построчном порядке. Рассмотрим следующий пример:
$a = "red",$true
$b = (New-Object 'int[,]' 2,2)
$b[0,0] = 10
$b[0,1] = 20
$b[1,0] = 30
$b[1,1] = 40
$c = $a + $b
$a.GetType().Name
$b.GetType().Name
$c.GetType().Name
$c
Выходные данные показывают, что это 1-мерный массив, $c
содержащий элементы из $a
$b
и в основном порядке строк.
Object[]
Int32[,]
Object[]
red
True
10
20
30
40
Методы массивов
Clear
Задает все значения default элементов значением типа элемента массива. Метод Clear()
не сбрасывает размер массива.
В следующем примере $a
представлен массив объектов.
$a = 1, 2, 3
$a.Clear()
$a | % { $null -eq $_ }
True
True
True
В этом примере $intA
явно типизированный для хранения целых чисел.
[int[]] $intA = 1, 2, 3
$intA.Clear()
$intA
0
0
0
ForEach()
Позволяет выполнять итерацию по всем элементам массива и выполнять определенную операцию для каждого элемента массива.
Метод ForEach()
имеет несколько перегрузок, выполняющих различные операции.
ForEach(scriptblock expression)
ForEach(scriptblock expression, object[] arguments)
ForEach(type convertToType)
ForEach(string propertyName)
ForEach(string propertyName, object[] newValue)
ForEach(string methodName)
ForEach(string methodName, object[] arguments)
ForEach(выражение scriptblock)
ForEach(выражение scriptblock, object[] аргументы)
Этот метод был добавлен в PowerShell версии 4.
Примечание.
Для синтаксиса требуется использование блока скриптов. Круглые скобки являются необязательными, если блок скрипта является единственным параметром. Кроме того, между методом и открывающей скобкой не должно быть пробела.
В следующем примере показано, как использовать ForEach()
метод. В этом случае намерение состоит в создании квадратного значения элементов в массиве.
$a = @(0 .. 3)
$a.ForEach({ $_ * $_})
0
1
4
9
Как и параметр ForEach-Object
ArgumentList, arguments
параметр позволяет передавать массив аргументов в блок скрипта, настроенный для их принятия.
Дополнительные сведения о поведении ArgumentList см. в about_Splatting.
ForEach(тип convertToType)
Метод можно использовать для приведения элементов к другому типу. В ForEach()
следующем примере показано, как преобразовать список строковых дат в [DateTime]
тип.
("1/1/2017", "2/1/2017", "3/1/2017").ForEach([datetime])
Sunday, January 1, 2017 12:00:00 AM
Wednesday, February 1, 2017 12:00:00 AM
Wednesday, March 1, 2017 12:00:00 AM
ForEach(string propertyName)
ForEach(string propertyName, object[] newValue)
Метод ForEach()
также можно использовать для извлечения или задания значений свойств для каждого элемента в коллекции.
# Set all LastAccessTime properties of files to the current date.
(dir 'C:\Temp').ForEach('LastAccessTime', (Get-Date))
# View the newly set LastAccessTime of all items, and find Unique entries.
(dir 'C:\Temp').ForEach('LastAccessTime') | Get-Unique
Wednesday, June 20, 2018 9:21:57 AM
ForEach(string methodName)
ForEach(string methodName, object[] аргументы)
Наконец, ForEach()
методы можно использовать для выполнения метода для каждого элемента в коллекции.
("one", "two", "three").ForEach("ToUpper")
ONE
TWO
THREE
Как и параметр ForEach-Object
ArgumentList, arguments
параметр позволяет передавать массив значений в блок скрипта, настроенный для их принятия.
Примечание.
Начиная с Windows PowerShell 3.0, извлекая свойства и выполняя методы для каждого элемента в коллекции, также можно выполнить с помощью методов скалярных объектов и коллекций. Дополнительные сведения см. здесь about_Methods.
Where()
Позволяет фильтровать или выбирать элементы массива. Скрипт должен иметь значение, отличное от нуля (0), $false
пустой строки или $null
элемента, отображаемого Where()
после него. Дополнительные сведения о логическое вычисление см. в about_Booleans.
Для метода существует одно определение Where()
.
Where(scriptblock expression[, WhereOperatorSelectionMode mode
[, int numberToReturn]])
Примечание.
Для синтаксиса требуется использование блока скриптов. Круглые скобки являются необязательными, если блок скрипта является единственным параметром. Кроме того, между методом и открывающей скобкой не должно быть пробела.
Это Expression
блок скрипта, необходимый для фильтрации, mode
необязательный аргумент позволяет использовать дополнительные возможности выбора, а numberToReturn
необязательный аргумент позволяет ограничить количество возвращаемых элементов из фильтра.
Значение должно быть значением перечисления mode
WhereOperatorSelectionMode:
Default
(0
) — возврат всех элементовFirst
(1
) — возврат first элементаLast
(2
) — возврат last элементаSkipUntil
(3
) — условие пропуска элементов until имеет значение true, возвращает все остальные элементы (включая first элемент, для которого условие имеет значение true).Until
(4
) — возвращает условие "Все элементы until " trueSplit
(5
) — возврат массива двух элементов- Элемент first содержит соответствующие элементы
- Второй элемент содержит оставшиеся элементы
В следующем примере показано, как выбрать все нечетные числа из массива.
(0..9).Where{ $_ % 2 }
1
3
5
7
9
В следующем примере показано, как выбрать все непустые строки.
('hi', '', 'there').Where{ $_ }
hi
there
Default
Режим Default
фильтрует элементы с помощью скриптблока Expression
.
Если задано numberToReturn
значение, оно указывает максимальное количество возвращаемых элементов.
# Get the zip files in the current users profile, sorted by LastAccessTime
$Zips = dir $env:userprofile -Recurse '*.zip' | Sort-Object LastAccessTime
# Get the least accessed file over 100MB
$Zips.Where({$_.Length -gt 100MB}, 'Default', 1)
Примечание.
Как Default
режим, так и First
режим возвращают first элементы (numberToReturn
) и могут использоваться взаимозаменяемо.
Last
$h = (Get-Date).AddHours(-1)
$logs = dir 'C:\' -Recurse '*.log' | Sort-Object CreationTime
# Find the last 5 log files created in the past hour
$logs.Where({$_.CreationTime -gt $h}, 'Last', 5)
SkipUntil
Режим SkipUntil
пропускает все объекты в коллекции until , объект передает фильтр выражения блока скрипта. Затем он возвращает все оставшиеся элементы коллекции без тестирования. Тестируется только один проходящий элемент.
Это означает, что возвращаемая коллекция содержит как передача, так и непередаваемые элементы, которые не были протестированы.
Количество возвращаемых элементов может быть ограничено путем передачи значения аргументу numberToReturn
.
$computers = "Server01", "Server02", "Server03", "localhost", "Server04"
# Find the first available online server.
$computers.Where({ Test-Connection $_ }, 'SkipUntil', 1)
localhost
Until
Режим Until
инвертирует SkipUntil
режим. Он возвращает ВСЕ элементы в коллекции until , элемент передает выражение блока скрипта. После передачи выражения Where()
scriptblock метод останавливает обработку элементов.
Это означает, что first набор элементов, не проходящих из Where()
метода. После прохождения одного элемента остальные не проверяются или возвращаются.
Количество возвращаемых элементов может быть ограничено путем передачи значения аргументу numberToReturn
.
# Retrieve the first set of numbers less than or equal to 10.
(1..50).Where({$_ -gt 10}, 'Until')
# This would perform the same operation.
(1..50).Where({$_ -le 10})
1
2
3
4
5
6
7
8
9
10
Примечание.
Оба Until
и SkipUntil
работают в локальной среде НЕ тестируют пакет элементов.
Until
возвращает элементы ДО firstPASS. SkipUntil
возвращает все элементы ПОСЛЕ прохожденияfirst, включая передаваемый first элемент.
Split
Режим Split
разделяет элементы коллекции или группирует их на две отдельные коллекции. Те, которые передают выражение scriptblock, и те, которые не выполняются.
numberToReturn
Если задано, first коллекция содержит передаваемые элементы, а не превышает указанное значение.
Остальные объекты, даже те, которые передают фильтр выражений, возвращаются во второй коллекции.
$running, $stopped = (Get-Service).Where({$_.Status -eq 'Running'}, 'Split')
$running
Status Name DisplayName
------ ---- -----------
Running Appinfo Application Information
Running AudioEndpointBu... Windows Audio Endpoint Builder
Running Audiosrv Windows Audio
...
$stopped
Status Name DisplayName
------ ---- -----------
Stopped AJRouter AllJoyn Router Service
Stopped ALG Application Layer Gateway Service
Stopped AppIDSvc Application Identity
...
Примечание.
Where()
Оба ForEach()
метода являются встроенными элементами. Дополнительные сведения о встроенных элементах см. в этой статье.
Получение элементов массива
Чтобы получить свойства и методы массива, такие как Length свойство и метод SetValue , используйте параметр InputObject командлета Get-Member
.
При отправке массива Get-Member
в PowerShell отправляет элементы по одному за раз и Get-Member
возвращает тип каждого элемента в массиве (игнорируя повторяющиеся).
При использовании параметра Get-Member
InputObject возвращает элементы массива.
Например, следующая команда получает члены переменной массива $a
.
Get-Member -InputObject $a
Вы также можете получить члены массива, введя запятую (,
) перед значением, которое будет передано командлету Get-Member
. Запятая делает массив вторым элементом в массиве массивов. PowerShell передает массивы по одному и Get-Member
возвращает элементы массива. Как и в следующих двух примерах.
,$a | Get-Member
,(1,2,3) | Get-Member
Управление массивом
Элементы в массиве можно изменить, добавить элемент в массив и объединить значения из двух массивов в третий массив.
Чтобы изменить значение определенного элемента в массиве, укажите имя массива и индекс элемента, который требуется изменить, а затем используйте оператор присваивания (=
) для указания нового значения элемента. Например, чтобы изменить значение второго элемента в массиве $a
(позиция индекса 1) на 10, введите:
$a[1] = 10
Для изменения значения можно также использовать метод SetValue массива. Следующий пример изменяет второе значение (позицию индекса 1) массива $a
на 500:
$a.SetValue(500,1)
Оператор можно использовать +=
для добавления элемента в массив. В следующем примере показано, как добавить элемент в $a
массив.
$a = @(0..4)
$a += 5
Примечание.
При использовании +=
оператора PowerShell фактически создает новый массив со значениями исходного массива и добавленным значением. Это может привести к проблемам с производительностью, если операция повторяется несколько раз или размер массива слишком велик.
Несложно удалить элементы из массива, но можно создать новый массив, содержащий только выбранные элементы существующего массива. Например, чтобы создать $t
массив со всеми элементами в $a
массиве, за исключением значения в позиции индекса 2, введите:
$t = $a[0,1 + 3..($a.length - 1)]
Чтобы объединить два массива в один массив, используйте оператор plus (+
). В следующем примере создаются два массива, объединяются и отображаются итоговые объединенные массивы.
$x = 1,3
$y = 5,9
$z = $x + $y
В результате массив $z
содержит 1, 3, 5 и 9.
Чтобы удалить массив, назначьте значение $null
массиву. Следующая команда удаляет массив в переменной $a
.
$a = $null
Можно также использовать Remove-Item
командлет, но назначение значения $null
выполняется быстрее, особенно для больших массивов.
Массивы из нуля или одного
Начиная с Windows PowerShell 3.0 коллекция из нуля или одного объекта имеет Count свойства и Length свойства. Кроме того, можно индексировать массив одного объекта. Эта функция помогает избежать ошибок сценариев, возникающих при выполнении команды, ожидающей, что коллекция получает менее двух элементов.
Примечание.
В Windows PowerShell объекты, созданные путем приведения хэш-файла , не [pscustomobject]
имеют Length свойств или Count свойств.
Попытка получить доступ к этим членам возвращается $null
.
В следующем примере показано, что переменная, содержащая объекты, не имеет Count значения Length 0 и 0.
PS> $a = $null
PS> $a.Count
0
PS> $a.Length
0
В следующем примере показано, что переменная, содержащая один объект, имеет Count значение Length 1. Можно также использовать индексирование массивов для доступа к значению объекта.
PS> $a = 4
PS> $a.Count
1
PS> $a.Length
1
PS> $a[0]
4
PS> $a[-1]
4
При выполнении команды, которая может возвращать коллекцию или один объект, можно использовать индексирование массивов для доступа к значению объекта без необходимости проверять Count Length или свойства. Однако если результатом является один объект (singleton), а этот объект имеет Count или Length свойство, значение этих свойств принадлежит одному объекту и не представляет количество элементов в коллекции.
В следующем примере команда возвращает один строковый объект. Эта Length строка имеет значение 4
.
PS> $result = 'one','two','three','four' | Where-Object {$_ -like 'f*'}
PS> $result.GetType().FullName
System.String
PS> $result
four
PS> $result.Count
1
PS❯ $result.Length
4
Если вы хотите $result
быть массивом строк, необходимо объявить переменную в виде массива.
В этом примере $result
представляет собой массив строк. И Count массив имеет 1
значение , а Length first элемент имеет значение 4
.Length
PS> [string[]]$result = 'one','two','three','four' |
Where-Object {$_ -like 'f*'}
PS> $result.GetType().FullName
System.String[]
PS> $result
four
PS> $result.Count
1
PS> $result.Length
1
PS> $result[0].Length
4
Индексирование типов .NET, реализующих IDictionary<TKey, TValue>
PowerShell не вызывает истинный индексатор типа для типов, реализующих универсальный IDictionary<TKey, TValue>
интерфейс. Вместо этого при указании ключа PowerShell проверяет наличие ключа, TryGetValue()
который возвращает $null
, когда ключ не существует.
В отличие от этого, если вызвать истинный индексатор типа, Item(<key>)
метод создает исключение, если ключ не существует.
В следующем примере демонстрируется это различие.
PS> [Collections.Generic.Dictionary[string, int]]::new()['nosuchkey']
# No output ($null)
PS> [Collections.Generic.Dictionary[string, int]]::new().Item('nosuchkey')
Exception getting "Item": "The given key 'nosuchkey' was not present in the dictionary."
At line:1 char:1
+ [Collections.Generic.Dictionary[string, int]]::new().Item('nosuchkey' ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], GetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenGetting
Перечисление доступа к членам
Начиная с PowerShell 3.0 при использовании оператора доступа к члену для доступа к элементу, который не существует в коллекции списков, PowerShell автоматически перечисляет элементы в коллекции и пытается получить доступ к указанному элементу на каждом элементе. Дополнительные сведения см. в разделе about_Member-Access_Enumeration.
Примеры
В следующем примере создаются два новых файла и хранятся полученные объекты в переменной $files
массива. Так как объект массива не имеет члена LastWriteTime, значение LastWriteTime возвращается для каждого элемента в массиве.
$files = (New-Item -Type File -Force '/temp/t1.txt'),
(New-Item -Force -Type File '/temp/t2.txt')
$files.LastWriteTime
Friday, June 25, 2021 1:21:17 PM
Friday, June 25, 2021 1:21:17 PM
Перечисление доступа к члену позволяет получать значения из элементов коллекции, но не задавать значения элементов в коллекции. Например:
$files.LastWriteTime = (Get-Date).AddDays(-1)
The property 'LastWriteTime' cannot be found on this object. Verify that the
property exists and can be set.
At line:1 char:1
+ $files.LastWriteTime = (Get-Date).AddDays(-1)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
Чтобы задать значения, необходимо использовать метод.
$files.set_LastWriteTime((Get-Date).AddDays(-1))
$files.LastWriteTime
Thursday, June 24, 2021 1:23:30 PM
Thursday, June 24, 2021 1:23:30 PM
Метод set_LastWriteTime()
является скрытым членом объекта FileInfo. В следующем примере показано, как найти скрытые set
методы.
$files | Get-Member -Force -Name set_*
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Attributes Property System.IO.FileAttributes Attributes {get;set;}
CreationTime Property datetime CreationTime {get;set;}
CreationTimeUtc Property datetime CreationTimeUtc {get;set;}
IsReadOnly Property bool IsReadOnly {get;set;}
LastAccessTime Property datetime LastAccessTime {get;set;}
LastAccessTimeUtc Property datetime LastAccessTimeUtc {get;set;}
LastWriteTime Property datetime LastWriteTime {get;set;}
LastWriteTimeUtc Property datetime LastWriteTimeUtc {get;set;}
Внимание
Так как метод выполняется для каждого элемента в коллекции, необходимо учитывать при вызове методов с помощью перечисления элементов.
См. также
PowerShell