Los Arrays en Powershell
Vamos a ver una serie de consideraciones sobre cómo opera Powershell con los arrays o colecciones de objetos, lo que implica su definición, modificación de elementos, agregar o quitar elementos y cómo afecta todo esto a los tipos de datos. Estos son los puntos que trataremos:
Definición de variables de tipo array
Definición de matrices
Acceder y modificar los elementos de un array
Agregar elementos a un array
Las variables a las que se les asignan arrays, al igual que el resto de variables en Powershell, no es necesario definirlas, pues la propia asignación del array a una variable inexistente se convierte a la vez en su definición. Powershell considera array a toda colección de objetos, ya sean escalares u otro tipo de objetos. Los elementos de estas colecciones de objetos pueden estar separados por comas, estar expresados por el operador de rango o ser el resultado de alguna expresión que devuelva una colección, en las cuales puede haber o no encaminamiento.
Así pues, para definir un array de la forma más simple basta con hacer lo siguiente:
PS> $Enteros = 1,2,3,4,5
PS> $Cadenas = "a","b","c","d","e"
En las dos líneas anteriores se han definido dos arrays, uno con enteros y otro con cadenas, utilizando el operador coma, que permite definir los elementos de un array separados por comas. En el caso de números enteros se podría haber definido el array de usando el operador de rango:
PS> $EnterosRango = 1..5
PS> $EnterosRango
1
2
3
4
5
Nota: El operador de rango representa una secuencia de enteros, con los límites superior e inferior separados por dos puntos decimales. Permite expresar el rango en orden ascendente o descendente, así como permite que los límites inferior o superior sean establecidos por medio de una expresión que tenga como resultado un entero o por variables que contengan enteros: PS> 1..5 1 2 3 4 5 PS> 5..1 5 4 3 2 1 PS> 1..(3+2) 1 2 3 4 5 PS> $Inicio = 1 PS> $Final = 5 PS> $Inicio..$Final 1 2 3 4 5 |
Como hemos dicho anteriormente, el array puede ser fruto de una expresión que devuelva una colección:
PS> $EnterosFor = @(For($i;$i -lt 6;$i++){$i})
En este caso, hemos definido el array con un bucle For que va añadiendo al array los valores del 1 al 5, obteniendo un array idéntico a los que definimos antes con los escalares separados por comas o con el operador de rango:
PS> $Enteros
1
2
3
4
5
PS> $EnterosRango
1
2
3
4
5
PS> $EnterosFor
1
2
3
4
5
La expresión de asignación a una variable puede contener encaminamiento:
PS> $ProcesosIdAlto = Get-Process | Sort-Object Id -Descending | Select-Object -First 10
PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
216 11 1752 6256 33 5776 OSE
346 23 64824 71304 611 2,23 5316 powershell
318 24 4580 1472 87 5308 wmpnetwk
1095 105 79464 118712 384 39,22 4924 iexplore
191 13 3852 14000 95 0,45 4904 RuntimeBroker
124 12 6520 6920 55 4820 iPodService
262 22 5528 11120 122 0,08 4680 iTunesHelper
901 153 276724 317356 587 291,82 4592 iexplore
417 83 515448 504396 674 852,39 4576 opera
759 58 21952 51824 282 6,65 4532 iexplore
En el anterior ejemplo, hemos definido un array con objetos no escalares, en este caso objetos del tipo System.Diagnostics.Process, que es el tipo de objetos que devuelve el Cmdlet Get-Process. Para definirlo obtenemos todos los procesos del sistema con Get-Process, los ordenamos en función a su Id con orden descendente y nos quedamos con los 10 primeros.
Otra cosa que se puede hacer es definir un array vacío, sin elementos, o un array de un único elemento. Esto se hace de la utilizando el operador de sub-expresión de array:
PS> $ArrayVacio = @()
PS> $ArrayVacio
PS> $ArrayUnElemento = @(1)
PS> $ArrayUnElemento
1
PS> $ArrayVacio.Length
0
PS> $ArrayUnElemento.Length
1
Nota: Un array se puede construir con el operador de sub-expresión de array, que responde a la sintaxis: @(expresión) Esto significa que se puede encerrar entre @() cualquier expresión, o incluso nada, y el array tendrá los elementos que devuelva esa expresión. La expresión puede ser compuesta por uno o varios escalares o la devolución de un Cmdlet, por ejemplo. Así pues, usar el operador de sub-expresión de array permite crear arrays vacíos o de un único elemento, así como crear arrays con la devolución de determinado comando: PS> $UnElemento = 1 PS> $UnElemento.GetType().FullName System.Int32 PS> $UnElemento = @(1) PS> $UnElemento.GetType().FullName System.Object[] PS> $Vacio = @() PS> $Vacio.GetType().FullName System.Object[] PS> $UnElemento.Length 1 PS> $Vacio.Length 0 PS> $Discos = @(Get-WmiObject Win32_LogicalDisk) PS> $Discos.Length 4 PS> $Discos.GetType().FullName System.Object[] Esto no significa que sea necesario usar el operador de sub-expresión de array para definir un array, pues en el momento en el que estemos asignando a una variable una colección, ya sea de escalares como de otro tipo de objetos, esa variable será un array; el uso de @(expresión) nos permite asegurarnos de que estamos asignando un array, pues si, por ejemplo, la devolución de una expresión compleja sólo devuelve un elemento o ninguno, la variable no será un array, y usando @(expresión) sí lo será: PS> $OS = Get-WmiObject Win32_OperatingSystem PS> $OS.GetType().FullName System.Management.ManagementObject PS> $OS = @(Get-WmiObject Win32_OperatingSystem) PS> $OS.GetType().FullName System.Object[] PS> PS> $Services = Get-Service PS> $Services.GetType().FullName System.Object[] PS> $Services[0].GetType().FullName System.ServiceProcess.ServiceController PS> $Services = @(Get-Service) PS> $Services.GetType().FullName System.Object[] PS> $Services[0].GetType().FullName System.ServiceProcess.ServiceController En los ejemplos anteriores, podemos observar cómo una consulta WMI sobre la clase Win32_OperatingSystem (qué sólo devolverá una instancia de la misma) cuando es asignada directamente devolverá un objeto del tipo que devuelve la consulta (System.Management.ManagementObject) mientras que al comprender la expresión con el operador de sub-expresión de array el tipo de la variable es System.Object[]. Por otra parte, se puede ver que si se asigna a una variable una expresión que como resultado da una colección de objetos (en el ejemplo son objetos System.ServiceProcess.ServiceController), el tipo de la variable será System.Object[], sin importar que se use o no el operador de sub-expresión de array. Así pues, el uso del operador de sub-expresión de array está indicado en estos casos: · Cuando queremos definir un array vacío (por ejemplo antes de un bucle for dentro del cual una instrucción condicional decidirá si se agrega o no algo al array). · Cuando queremos definir un array de un solo elemento. · Cuando queremos definir un array como fruto de una expresión de la cual no sabemos si devolverá varios elementos, uno solo o ninguno.
|
Por supuesto, todas las definiciones podrían haber sido hechas de una manera más “formal” utilizando el Cmdlet New-Variable (en todos los casos se ha usado el modificador Force, que implica que si la variable existe se sobrescriba, pues de no usarlo y existir se produce un error (esto lo hemos hecho así para poder seguir en la misma sesión de Powershell en la que previamente definimos las variables con asignación directa, por lo que ya existen):
PS> New-Variable -Name Enteros -Value 1,2,3,4,5 -Force
PS> New-Variable -Name Cadenas -Value "a","b","c","d","e" –Force
PS> New-Variable -Name EnterosRango -Value (1..5) -Force
PS> New-Variable -Name EnterosFor -value (. {For($i=1;$i -lt 6;$i++){$i}}) -Force
PS> New-Variable -Name ProcesosIdAlto -Value (. {Get-Process | Sort-Object Id -Descending | Select-Object -First 10}) –Force
PS> New-Variable –Name ArrayVacio –Value @() –Force
PS> New-Variable –Name UnElemento –Value @(1) -Force
Nota: Fijémonos cómo se ha definido la variable ProcesosIdAlto. Ha sido necesario pasar al parámetro Value la misma expresión, con la que asignamos el valor a esta variable previamente, como bloque de código, en contra de lo que más de alguno podría haber pensado que se realizaría por encaminamiento, pues al leer la ayuda de New-Variable, podemos ver cómo el parámetro Value acepta encaminamiento. Esto es debido a que cuando asignamos el valor directamente, la expresión, que contiene encaminamiento, al estar ubicada a la derecha de la asignación provoca que se asignen todos los resultados. Sin embargo ¿qué sucede al encaminar esta expresión hacia New-Variable?: PS> Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variable -Name ProcesosIdAlto –Force PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 900 161 285176 318268 593 564,51 4592 iexplore PS> ¿Qué ha sucedido, por qué sólo aparece un proceso en lugar de 10? Esto es debido a que New-Variable está situado a la derecha de la canalización, lo que provoca que sea invocado por cada elemento que se procesa, debido a que las canalizaciones van pasando elemento a elemento de un Cmdlet a otro; como se usa el modificador Force en New-Variable, se va creando de nuevo la variable con cada elemento recibido, sobrescribiendo el anterior, de manera que al terminar la variable sólo contiene el último elemento recibido. Esto se ve que es así de varias formas. Por ejemplo, usando el modificador Passthru, que provoca que se vaya mostrando por pantalla cada elemento procesado. En el ejemplo que se ve a continuación, se puede observar cómo se han ido pasando los 10 procesos a New-Variable y cómo es sólo el último el que al final contiene la variable creada: PS> Remove-Variable ProcesosIdAlto PS> Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variable -Name ProcesosIdAlto -Force -PassThru
Name Value ---- ----- ProcesosIdAlto System.Diagnostics.Process (powershell) ProcesosIdAlto System.Diagnostics.Process (wmpnetwk) ProcesosIdAlto System.Diagnostics.Process (iexplore) ProcesosIdAlto System.Diagnostics.Process (RuntimeBroker) ProcesosIdAlto System.Diagnostics.Process (iPodService) ProcesosIdAlto System.Diagnostics.Process (plugin-container) ProcesosIdAlto System.Diagnostics.Process (iTunesHelper) ProcesosIdAlto System.Diagnostics.Process (iexplore) ProcesosIdAlto System.Diagnostics.Process (FlashPlayerPlugin_11_4_402_287) ProcesosIdAlto System.Diagnostics.Process (opera)
PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 422 86 536528 513452 694 2.584,34 4576 opera Otra forma de verlo es quitando el modificador Force, lo que provocará un error en cada elemento posterior al primero, y al final del proceso la variable contendrá únicamente el primer valor: PS> Remove-Variable ProcesosIdAlto PS> Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variable -Name ProcesosIdAlto New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 547 31 65532 79904 635 20,59 5316 powershell Incluso podemos hacer ambas cosas a la vez (quitar Force y agregar Passthru) con lo que observaremos cómo el primer elemento ha sido procesado pero no así los siguientes: PS > Remove-Variable ProcesosIdAlto PS> Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variable -Name ProcesosId Alto -PassThru
Name Value ---- ----- ProcesosIdAlto System.Diagnostics.Process (powershell) New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
New-Variable : Ya existe una variable con el nombre 'ProcesosIdAlto'. En línea: 1 Carácter: 70 + Get-Process | Sort-Object Id -Descending | Select-Object -First 10 | New-Variabl ... + ~~~~~~~~~~~ + CategoryInfo : ResourceExists: (ProcesosIdAlto:String) [New-Variable], SessionStateException + FullyQualifiedErrorId : VariableAlreadyExists,Microsoft.PowerShell.Commands.NewVariableCommand
PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 454 32 67340 81604 636 20,72 5316 powershell
|
Para definir una matriz tenemos también diferentes maneras, pero todo se reduce, en realidad, a asignar a la variable tantos arrays como dimensiones queramos que tenga. Así pues podemos hacer lo siguiente:
PS P:\> $MatrizDeEnteros = (1,2,3,4),(5..8),@(For($i=9;$i -le 12;$i++){$i})
PS P:\> $MatrizDeEnteros[0]
1
2
3
4
PS P:\> $MatrizDeEnteros[1]
5
6
7
8
PS P:\> $MatrizDeEnteros[2]
9
10
11
12
En este ejemplo, hemos cubierto tres posibilidades a la hora de definir un array, como una sucesión de elementos utilizando el operador coma, como una sucesión de elementos utilizando el operador rango y como resultado de una expresión. Destacar que hemos especificado cada uno de los array encerrándolos entre paréntesis. Realmente, de forma más escrupulosa, deberíamos haber encerrado los dos primeros arrays, al igual que hemos hecho con el bucle For del tercero, en el operador de sub-expresión de array:
PS P:\> $MatrizDeEnteros = @(1,2,3,4),@(5..8),@(For($i=9;$i -le 12;$i++){$i})
PS P:\> $MatrizDeEnteros[0]
1
2
3
4
PS P:\> $MatrizDeEnteros[1]
5
6
7
8
PS P:\> $MatrizDeEnteros[2]
9
10
11
12
Veremos cómo acceder a todos los elementos del array, a uno o a varios elementos. Así mismo, veremos cómo podemos establecer el valor de un elemento del array. Así mismo, veremos cómo averiguar el número de elementos del array.
Para averiguar el número de elementos de un array, podemos consultar sus propiedades Length o Count (Count es un alias de Length):
PS> $Enteros.Length
5
PS> $Enteros.Count
5
En el caso de las matrices, esta propiedad Length la poseen tanto la matriz como cada uno de los elementos de las dimensiones:
PS> $MatrizdeEnteros = (((1..5),(6..10)),((11..15),(16..20)))
PS> $MatrizDeEnteros.Length
2
PS> $MatrizDeEnteros[0].Length
2
PS> $MatrizDeEnteros[0][0].Length
5
Como se puede ver, la propiedad Length de la matriz muestra el número de elementos de un array, pues eso es lo que en realidad es una matriz en Powershell, un array que contiene arrays como elementos, que a su vez pueden ser arrays. La propiedad Length aplicada a un elemento de la primera dimensión devuelve el número de elementos que contiene, en este caso dos arrays, y lo mismo sucede al consultar la propiedad Length de la segunda dimensión.
Para acceder al array completo, es tan sencillo como invocarlo:
PS> $Enteros = 1..5
PS> $Enteros
1
2
3
4
5
Es también posible, gracias a la propiedad Length, recorrer los elementos de un array por medio de un bucle For:
PS> For($i = 0;$i -lt $Enteros.Length;$i++){$Enteros[$i]}
1
2
3
4
5
Otra forma de recorrer el array es por medio de un bucle ForEach:
PS> ForEach($Entero In $Enteros){$Entero}
1
2
3
4
5
O también podemos usar el operador de rango y la propiedad Lenght menos uno, ya que los arrays en Powershell siempre están en base 0:
PS> $Enteros[0..($Enteros.Length - 1)]
1
2
3
4
5
En el caso de las matrices esto es más complejo, pues es necesario indicar explícitamente cada uno de los elementos de cada una de las dimensiones precedentes a la última:
PS> $MatrizDeEnteros = (((1..5),(6..10)),((11..15),(16..20)))
PS> For($i=0;$i -lt $MatrizDeEnteros.Length;$i++)
>> {
>> For($j=0;$j -lt $MatrizDeEnteros[$i].Length;$j++)
>> {
>> $MatrizDeEnteros[$i][$j]
>> }
>> }
>>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
¿Qué sucede si el array está contenido en una cadena? La respuesta es que se mostrarán los elementos no con salto de línea entre ellos, si no usando el espacio como separador, por ser el espacio el separador predeterminado de elementos de un array cuando está contenido dentro de una cadena:
PS> "$Enteros"
1 2 3 4 5
Por supuesto, esto sólo funciona con matrices si se hacen bucles, como se vio antes. Así pues, esto se puede hacer, por ejemplo, poniendo en un bloque el bucle que se vio antes e invocarlo como expresión dentro de la cadena:
PS> $Bloque = {
>> $MatrizDeEnteros = (((1..5),(6..10)),((11..15),(16..20)))
>> For($i=0;$i -lt $MatrizDeEnteros.Length;$i++)
>> {
>> For($j=0;$j -lt $MatrizDeEnteros[$i].Length;$j++)
>> {
>> $MatrizDeEnteros[$i][$j]
>> }
>> }
>> }
>>
PS> "$(. $Bloque)"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Nota: Se puede cambiar el separador dentro de una cadena de los elementos de los arrays para que sea lo que se necesite en lugar del espacio. Esto se realiza por medio de la variable OFS. De entrada, si consultamos la existencia de OFS vemos que la respuesta es negativa: PS> Test-Path variable:ofs False Si la creamos, el separador será lo que establezcamos en la misma. Por ejemplo, pondremos una arroba como separador: PS> "$Enteros" 1 2 3 4 5 PS> $ofs = "@" PS> "$Enteros" 1@2@3@4@5 En el ejemplo anterior vemos cómo al no estar definida la variable OFS el separador es un espacio; una vez definida vemos cómo el separador es una arroba, pues la arroba es lo que establecimos como valor de OFS. Nada hay que impida que sea una cadena de más de un carácter el separador de los elementos de los arrays cuando son mostrados dentro de una cadena: PS> $ofs = "`n`t" PS> "Eelementos de `$Enteros:`n`t$Enteros" Eelementos de $Enteros: 1 2 3 4 5 En el ejemplo anterior, hemos mostrado los elementos con un salto de línea entre ellos y desplazados un tabulador. Para devolver a Powershell el comportamiento predeterminado, o establecemos el espacio como valor de OFS o, mejor aún, eliminamos la variable: PS> Test-Path variable:ofs True PS> Remove-Variable ofs PS> Test-Path variable:ofs False PS> "$Enteros" 1 2 3 4 5 |
Para acceder a un elemento de un array se pone, a continuación del nombre del array, su índice encerrado entre corchetes:
PS> $Enteros[0]
1
PS> $Enteros[2]
3
Los arrays de Powershell tienen como base el cero, esto quiere decir que su primer elemento es el cero y su último elemento es su propiedad Length menos uno:
PS> $Enteros[$Enteros.Length - 1]
5
No obstante, si queremos obtener el último elemento de un array no es necesario hacer la operación anterior, es tan sencillo como poner de índice -1:
PS> $Enteros[-1]
5
Este signo menos indica que se está indizando de forma inversa, lo que permite obtener el segundo, tercero, etc., empezando por el final:
PS> $Enteros[-1]
5
PS> $Enteros[-2]
4
PS> $Enteros[-3]
3
PS> $Enteros[-4]
2
PS> $Enteros[-5]
1
Para modificar un elemento de un array es tan simple como asignar el nuevo valor al array con el índice correspondiente:
PS> $Enteros[0] = “Uno”
PS> “$Enteros”
Uno 2 3 4 5
PS> $Enteros [0] = 1
PS> “$Enteros”
1 2 3 4 5
Otra forma de modificar un elemento de un array es usando el método SetValue del array, cuya sintaxis es:
Array.SetValue(valor,índice)
Así pues:
PS> $Enteros.SetValue(“Uno”,0)
PS> “$Enteros”
Uno 2 3 4 5
PS> $Enteros.SetValue(1,0)
PS> “$Enteros”
1 2 3 4 5
Dado que se puede utilizar el operador coma para obtener varios elementos de un array, la sintaxis para acceder a los elementos de una matriz no puede ser como en la mayoría de lenguajes de la forma:
Matriz[Dimensión1,Dimensión2,…,DimesiónN]
Powershell entendería en este caso que se quieren obtener todos los elementos de la primera dimensión de la matriz para los índices de esta primera dimensión que estén expresados por Dimensión1, Dimensión2 y Dimensión3:
PS> $MatrizDeEnteros = ((1..5),(6..10),(11..15))
PS> $MatrizDeEnteros[0,1,2]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
PS> $MatrizDeEnteros[0,0,0]
1
2
3
4
5
1
2
3
4
5
1
2
3
4
5
Por tanto, la sintaxis de Powershell para acceder a los elementos en las matrices es la siguiente:
Matriz[Dimensión1][Dimensión2]…[DimensiónN]
PS> $MatrizDeEnteros[0][0][0]
1
Para acceder a varios elementos de un array, se pueden establecer los índices por medio del operador coma:
PS> $Enteros[1,3]
2
4
También se puede utilizar el operador rango:
PS> $Enteros[1..3]
2
3
4
Incluso se pueden combinar ambos por medio de la utilización del operador +:
PS> $Enteros = 1..10
PS> $Enteros
1
2
3
4
5
6
7
8
9
10
PS> $Enteros[0,1,2+7..9]
1
2
3
8
9
10
PS> $Enteros[0..3+5,7,9]
1
2
3
4
6
8
10
PS> $Enteros[0,1+3..5+7,9]
1
2
4
5
6
8
10
PS> $Enteros = 1..5
Para estos ejemplos, hemos redefinido el array $Enteros para que tenga 10 elementos, y así poder “jugar” mejor con él. En primer lugar mostramos los elementos de índice 0, 1,2 y de 7 a 9 (esto es, 7,8 y 9). En segundo lugar hemos mostrados los elementos de índices del 0 al 3 (esto es 0, 1, 2 y 3) y los elementos 5, 7 y 9. En tercer lugar, hemos mostrado los elementos de índice 0 y 1, del 3 al 5 y los elementos 7 y 9. En la última línea hemos restablecido el array al original de cinco elementos con el que hemos estado poniendo los ejemplos desde el principio.
También se pueden usar índices negativos en los rangos, para poder acceder a un conjunto de elementos desde el final hasta el principio:
PS> $Enteros[-3..-1]
3
4
5
Conviene señalar que no podemos establecer el rango 0..-2 para obtener todos los elementos del array menos el último, pues en realidad esto devuelve el primero, el último y el penúltimo:
PS> $Enteros[0..-2]
1
5
4
Así mismo, tampoco obtendremos todos los elementos del array con el rango 0..-1, sólo obtendremos el primero y el último:
PS> $Enteros[0..-1]
1
5
¿Por qué sucede esto? Es bien sencillo; 1..5 significa de 1 a 5, -1..-5 significa de -1 a -5, por ello 0..-1 significa de 0 a – 1, lo que es 0 y -1; 0..-3 significa 0, -1, -2 y -3. Así pues:
PS> $Enteros[0..-3]
1
5
4
3
PS> $Enteros[0..-4]
1
5
4
3
2
Obviamente, el rango m..-n devolverá los m primeros elementos, en orden inverso, seguidos de los n últimos elementos en orden inverso:
PS> $Enteros[4..-5]
5
4
3
2
1
5
4
3
2
1
PS> $Enteros[3..-4]
4
3
2
1
5
4
3
2
Una vez tenemos el array, habrá que ver qué tipo de datos contiene y qué tipo de datos es el array. Pongamos como ejemplo el array $Enteros; si lo encaminamos a Get-Member observaremos que el tipo de datos es System.Int32:
PS> $Enteros | Get-Member
TypeName: System.Int32
Name MemberType Definition
---- ---------- ----------
CompareTo Method int CompareTo(System.Object value), int CompareTo(int value), int IComparable.CompareTo(Syste...
Equals Method bool Equals(System.Object obj), bool Equals(int obj), bool IEquatable[int].Equals(int other)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
GetTypeCode Method System.TypeCode GetTypeCode(), System.TypeCode IConvertible.GetTypeCode()
ToBoolean Method bool IConvertible.ToBoolean(System.IFormatProvider provider)
ToByte Method byte IConvertible.ToByte(System.IFormatProvider provider)
ToChar Method char IConvertible.ToChar(System.IFormatProvider provider)
ToDateTime Method datetime IConvertible.ToDateTime(System.IFormatProvider provider)
ToDecimal Method decimal IConvertible.ToDecimal(System.IFormatProvider provider)
ToDouble Method double IConvertible.ToDouble(System.IFormatProvider provider)
ToInt16 Method int16 IConvertible.ToInt16(System.IFormatProvider provider)
ToInt32 Method int IConvertible.ToInt32(System.IFormatProvider provider)
ToInt64 Method long IConvertible.ToInt64(System.IFormatProvider provider)
ToSByte Method sbyte IConvertible.ToSByte(System.IFormatProvider provider)
ToSingle Method float IConvertible.ToSingle(System.IFormatProvider provider)
ToString Method string ToString(), string ToString(string format), string ToString(System.IFormatProvider pro...
ToType Method System.Object IConvertible.ToType(type conversionType, System.IFormatProvider provider)
ToUInt16 Method uint16 IConvertible.ToUInt16(System.IFormatProvider provider)
ToUInt32 Method uint32 IConvertible.ToUInt32(System.IFormatProvider provider)
ToUInt64 Method uint64 IConvertible.ToUInt64(System.IFormatProvider provider)
Vale, esto quiere decir que es un array de enteros ¿verdad? Pues no, ya que Get-Member nos ha pasado el tipo de datos que ha encontrado dentro del array, pero no el del array en sí. Que los elementos del array sean del tipo System.Int32 no significa que el array sea del tipo System.Int32[]. Si ejecutamos el método GetType del array y obtenemos el nombre completo del tipo obtendremos lo siguiente:
PS> $Enteros.GetType().FullName
System.Object[]
Nota: Si queremos averiguar los métodos y propiedades del array, en lugar de los de sus elementos, podemos pasar el array como parámetro InputObect a Get-Member: PS> Get-Member -InputObject $Enteros
TypeName: System.Object[]
Name MemberType Definition ---- ---------- ---------- Count AliasProperty Count = Length Add Method int IList.Add(System.Object value) Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a... Clear Method void IList.Clear() Clone Method System.Object Clone(), System.Object ICloneable.Clone() CompareTo Method int IStructuralComparable.CompareTo(System.Object other, System.Collections.ICo... Contains Method bool IList.Contains(System.Object value) CopyTo Method void CopyTo(array array, int index), void CopyTo(array array, long index), void... Equals Method bool Equals(System.Object obj), bool IStructuralEquatable.Equals(System.Object ... Get Method System.Object Get(int ) GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ... GetHashCode Method int GetHashCode(), int IStructuralEquatable.GetHashCode(System.Collections.IEqu... GetLength Method int GetLength(int dimension) GetLongLength Method long GetLongLength(int dimension) GetLowerBound Method int GetLowerBound(int dimension) GetType Method type GetType() GetUpperBound Method int GetUpperBound(int dimension) GetValue Method System.Object GetValue(Params int[] indices), System.Object GetValue(int index)... IndexOf Method int IList.IndexOf(System.Object value) Initialize Method void Initialize() Insert Method void IList.Insert(int index, System.Object value) Remove Method void IList.Remove(System.Object value) RemoveAt Method void IList.RemoveAt(int index) Set Method void Set(int , System.Object ) SetValue Method void SetValue(System.Object value, int index), void SetValue(System.Object valu... ToString Method string ToString() Item ParameterizedProperty System.Object IList.Item(int index) {get;set;} IsFixedSize Property bool IsFixedSize {get;} IsReadOnly Property bool IsReadOnly {get;} IsSynchronized Property bool IsSynchronized {get;} Length Property int Length {get;} LongLength Property long LongLength {get;} Rank Property int Rank {get;} SyncRoot Property System.Object SyncRoot {get;} Si queremos hacerlo por encaminamiento, podemos hacer al array miembro de otro array, para que así Get-Member lo considere un miembro del array y nos muestre sus propiedades y métodos. Esto se consigue poniendo el operador coma delante del array, con lo que Get-Member interpretará que recibe por encaminamiento un array con dos elementos, un nulo, del que no mostrará nada, y el propio array: PS> ,$Enteros | Get-Member
TypeName: System.Object[]
Name MemberType Definition ---- ---------- ---------- Count AliasProperty Count = Length Add Method int IList.Add(System.Object value) Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a... Clear Method void IList.Clear() Clone Method System.Object Clone(), System.Object ICloneable.Clone() CompareTo Method int IStructuralComparable.CompareTo(System.Object other, System.Collections.ICo... Contains Method bool IList.Contains(System.Object value) CopyTo Method void CopyTo(array array, int index), void CopyTo(array array, long index), void... Equals Method bool Equals(System.Object obj), bool IStructuralEquatable.Equals(System.Object ... Get Method System.Object Get(int ) GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ... GetHashCode Method int GetHashCode(), int IStructuralEquatable.GetHashCode(System.Collections.IEqu... GetLength Method int GetLength(int dimension) GetLongLength Method long GetLongLength(int dimension) GetLowerBound Method int GetLowerBound(int dimension) GetType Method type GetType() GetUpperBound Method int GetUpperBound(int dimension) GetValue Method System.Object GetValue(Params int[] indices), System.Object GetValue(int index)... IndexOf Method int IList.IndexOf(System.Object value) Initialize Method void Initialize() Insert Method void IList.Insert(int index, System.Object value) Remove Method void IList.Remove(System.Object value) RemoveAt Method void IList.RemoveAt(int index) Set Method void Set(int , System.Object ) SetValue Method void SetValue(System.Object value, int index), void SetValue(System.Object valu... ToString Method string ToString() Item ParameterizedProperty System.Object IList.Item(int index) {get;set;} IsFixedSize Property bool IsFixedSize {get;} IsReadOnly Property bool IsReadOnly {get;} IsSynchronized Property bool IsSynchronized {get;} Length Property int Length {get;} LongLength Property long LongLength {get;} Rank Property int Rank {get;} SyncRoot Property System.Object SyncRoot {get;}
|
Vemos que el array es de tipo System.Object[] ¿System.Object[] en lugar de System.Int32[]? ¿Por qué? La razón de esto está en el diseño del propio Powershell en lo que respecta a los arrays. Los arrays al ser creados lo son como objetos de tipo System.Object[], con lo que para conseguir un array System.Int32 es necesario establecer de forma explícita el tipo al array:
PS> [System.Int32[]] $Enteros = 1,2,3,4
PS> ($Enteros.GetType()).FullName
System.Int32[]
Ahora sí es un array de enteros, no como antes que era un array de objetos. Por cierto, que realmente no se ha establecido el tipo, si no que se ha cambiado (se ha hecho “Cast”), pues el valor asignado es también System.Object[]:
PS> (1,2,3,4).GetType().FullName
System.Object[]
El que un array al definirse sea de tipo System.Object[] tiene una implicación muy importante: un array de tipo System.Object[] puede contener cualquier tipo de dato en cualquiera de sus elementos, incluidos escalares, objetos de cualquier otro tipo e incluso otros arrays o colecciones. Veamos este ejemplo:
PS> $Variopinto = 1,"a",(Get-Process -Id 5316),(2,3,4,5)
PS> $Variopinto.GetType().FullName
System.Object[]
PS> For($i=0;$i -lt 4;$i++){
>> "Elemento $i`:“
>> “-- Valor`: $($Variopinto[$i])”
>> “-- Tipo`: $($Variopinto[$i].GetType().FullName)"
>> “”}
>>
Elemento 0:
-- Valor: 1
-- Tipo: System.Int32
Elemento 1:
-- Valor: a
-- Tipo: System.String
Elemento 2:
-- Valor: System.Diagnostics.Process (powershell)
-- Tipo: System.Diagnostics.Process
Elemento 3:
-- Valor: 2 3 4 5
-- Tipo: System.Object[]
Como podemos apreciar, el array que acabamos de definir es de tipo System.Object[], tiene como primer elemento un entero, de segundo una cadena, de tercero un objeto de tipo System.Diagnostics.Process y como cuarto y último un array que, como no ha sido cambiado su tipo al definirlo, es también de tipo System.Object[].
Antes vimos que Get-Member nos devolvía System.Int32 con $Enteros ¿Qué devuelve con $Variopinto? He aquí la respuesta:
PS> $Variopinto | Get-Member
TypeName: System.Int32
Name MemberType Definition
---- ---------- ----------
.
.
.
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
.
.
.
TypeName: System.Diagnostics.Process
Name MemberType Definition
---- ---------- ----------
.
.
.
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
.
.
.
Get-Member devuelve cada uno de los tipos que encuentre en el encaminamiento que recibe (para abreviar la salida hemos sustituido los nombres de métodos y propiedades que devuelve Get-Member por tres puntos). Como $Enteros sólo contiene enteros Get-Member sólo devuelve System.Int32, pero como $Variopinto tiene 4 clases distintas de objetos devuelve esas cuatro clases.
Resumiendo: un array en PowerShell es de tipo System.Object[], siempre que no se le cambie de forma explícita el tipo de dato por medio de una conversión; esto implica que se puede establecer cualquier tipo de objeto en cualquiera de sus elementos, incluidos escalares, otro tipo de objetos, arrays o colecciones.
En primer lugar, veamos cómo se agregan elementos a un array. Para agregar elementos se utiliza el operador +=:
PS> $Enteros
1
2
3
4
PS> $Enteros += 5
PS> $Enteros
1
2
3
4
5
Respecto a la segunda cuestión, parece una pregunta tonta: ¿qué importa si se agregan elementos a un array, dicho array será del mismo tipo que era antes, no? Veamoslo:
PS> [System.Int32[]] $Enteros = 1,2,3,4
PS> $Enteros.GetType().FullName
System.Int32[]
PS> $Enteros += 5
PS> $Enteros.GetType().FullName
System.Object[]
¿Cómo, se ha cambiado el tipo del array porque le hemos agregado un elemento? ¿Por qué? Nuevamente es un tema de diseño, pero no sólo de Powershell, si no de .NET Framework. En .NET Framework los arrays son estáticos, se declaran con una dimensión y ésta no puede ser cambiada. Por lo tanto, para poder incrementar un array realmente hay que crear uno nuevo con la nueva dimensión, copiar los elementos del original y establece el valor del nuevo elemento; una vez hecho esto se asigna el nuevo array a la variable que contenía el original, pero por el camino Powershell ha creado un nuevo array y por tanto su tipo es System.Object[]. Si queremos que al agregar un nuevo elemento a un array este no cambie su tipo es necesario que le cambiemos el tipo, de igual que hicimos al crearlo:
PS> [System.Int32[]] $Enteros = 1,2,3,4
PS> $Enteros.GetType().FullName
System.Int32[]
PS> [System.Int32[]] $Enteros += 5
PS> $Enteros.GetType().FullName
System.Int32[]
PS> $Enteros
1
2
3
4
5
Por desgracia, la respuesta es no, Powershell no establece un mecanismo transparente de quitar elementos a un array, como hace a la hora de agregarlos, lo que implica que para quitar elementos a un array es necesario realizarlo “con pico y pala”, esto es, creando un nuevo array al que sólo se le agregan los elementos deseados:
PS> $Enteros = $Enteros[0,2,4]
PS> $Enteros
1
3
5
En el ejemplo nos hemos quedado sólo con los impares; por supuesto, al hacer esto, realmente se ha creado un nuevo array y por tanto su tipo es System.Object[]; habría sido necesario hacer Cast para establecer otro tipo:
PS> [System.Int32[]] $Enteros = 1..5
PS> $Enteros.GetType().FullName
System.Int32[]
PS> $Enteros = $Enteros[0,2,4]
PS> $Enteros.GetType().FullName
System.Int32[]
PS> [System.Int32[]] $Enteros = 1..5
PS> $Enteros.GetType().FullName
System.Int32[]
PS> [System.Int32[]] $Enteros = $Enteros[0,2,4]
PS> $Enteros.GetType().FullName
System.Int32[]
La respuesta a esta pregunta es sí, utilizando el operador de suma:
PS> $Enteros1a5 = 1..5
PS> $Enteros6a10 = 6..10
PS> $Enteros1a10 = $Enteros1a5 + $Enteros6a10
PS> $Enteros1a10
1
2
3
4
5
6
7
8
9
10
Para el que lo pregunte, la respuesta es no, no se pueden restar arrays:
PS> $Enteros1a10 = $Enteros1a5 - $Enteros6a10
Error en la invocación del método porque [System.Object[]] no contiene ningún método llamado 'op_Subtraction'.
En línea: 1 Carácter: 1
+ $Enteros1a10 = $Enteros1a5 - $Enteros6a10
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Subtraction:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
La respuesta, en este caso es que sí, pero no de una forma automática, ha de hacerse utilizando el Cmdlet Sort-Object. Ordenar escalares no es complicado, se puede canalizar el array a Sort-Object y el resultado asignárselo al array:
PS> $Enteros = 3,2,5,1,4
PS> $Enteros
3
2
5
1
4
PS> $Enteros = $Enteros | Sort-Object
PS> $Enteros
1
2
3
4
5
En el ejemplo hemos ordenado enteros, podríamos ordenar también cadenas:
PS> $Cadenas = "c","e","b","d","a"
PS> $Cadenas
c
e
b
d
a
PS> $Cadenas = $Cadenas | Sort-Object
PS> $Cadenas
a
b
c
d
e
Como hemos mencionado antes, la canalización procesa elemento a elemento; si unimos esto a que Sort-Object ordena por la propiedad predeterminada de los objetos que recibe (propiedad Value en el caso de los escalares) se puede comprender que se puede ordenar una colección de objetos en función a una propiedad:
PS> $Enteros = 3,2,5,1,4
PS> $Enteros| Sort-Object Value
1
2
3
4
5
PS> $Cadenas = "c","e","b","d","a"
PS> $Cadenas| Sort-Object Value
a
b
c
d
e
PS> $ProcesosIdAlto = Get-Process | Sort-Object Id -Descending | Select-Object -First 10
PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
417 67 354864 356972 512 2.870,20 4980 opera
292 20 10700 19288 407 11,58 4956 CSISYN~1
386 93 190888 209540 497 ...81,73 4736 firefox
127 12 6528 6964 55 0,62 4388 iPodService
155 15 3236 7676 104 6,80 4180 NclIVTBTSrv
79 7 1200 3632 52 0,84 4140 NclUSBSrv64
584 66 27788 27224 547 5,35 4080 SearchIndexer
621 43 13756 3992 1014 5,69 4048 LiveComm
594 41 11852 26576 145 584,24 3812 SkyDrive
88 7 1364 5796 33 0,02 3756 dllhost
PS> $ProcesosIdAlto = $ProcesosIdAlto | Sort-Object VM
PS> $ProcesosIdAlto
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
88 7 1364 5796 33 0,02 3756 dllhost
79 7 1200 3632 52 0,84 4140 NclUSBSrv64
127 12 6528 6964 55 0,62 4388 iPodService
155 15 3236 7676 104 6,80 4180 NclIVTBTSrv
594 41 11852 26576 145 584,61 3812 SkyDrive
292 20 10700 19288 407 11,58 4956 CSISYN~1
386 93 190888 209540 497 ...90,14 4736 firefox
417 67 354864 356972 512 2.875,49 4980 opera
584 66 27788 27224 547 5,35 4080 SearchIndexer
621 43 13756 3992 1014 5,69 4048 LiveComm
En los ejemplos, hemos visto cómo al establecer Value como propiedad en la que se basa el ordenamiento, en el caso de los enteros y las cadenas, hemos obtenido el mismo resultado que si no se hubiera establecido nada; por otra parte, también hemos podido observar como la colección de procesos, que estaba ordenada en modo descendente en función de la propiedad Id, a pasado a estar ordenada en función de la propiedad VM.
Como hemos visto, no hay un mecanismo implantado para la eliminación de elementos, ni métodos para ordenarlos que no impliquen el uso de otro Cmdlet. Por ello, sobre todo cuando se quiere tener la posibilidad de quitar elementos, reordenar, etc., de una forma más simple, es recomendable el conocimiento y uso, cuando sea necesario de esta clase.
Para definir un ArrayList debemos usar el Cmdlet New-Object de la siguiente manera:
PS> $ArrayList = New-Object System.Collections.ArrayList
También se puede crear y asignar a la vez haciendo Cast a la asignación de un array:
PS> [System.Collections.ArrayList] $ArrayList = 1..5
PS> $ArrayList.GetType().FullName
System.Collections.ArrayList
Si usamos Get-Member con un array list obtendremos el siguiente resultado:
PS> Get-Member -InputObject $ArrayList
TypeName: System.Collections.ArrayList
Name MemberType Definition
---- ---------- ----------
Add Method int Add(System.Object value), int IList.Add(System.Object value)
AddRange Method void AddRange(System.Collections.ICollection c)
BinarySearch Method int BinarySearch(int index, int count, System.Object value, System.Collections....
Clear Method void Clear(), void IList.Clear()
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
Contains Method bool Contains(System.Object item), bool IList.Contains(System.Object value)
CopyTo Method void CopyTo(array array), void CopyTo(array array, int arrayIndex), void CopyTo...
Equals Method bool Equals(System.Object obj)
GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator ...
GetHashCode Method int GetHashCode()
GetRange Method System.Collections.ArrayList GetRange(int index, int count)
GetType Method type GetType()
IndexOf Method int IndexOf(System.Object value), int IndexOf(System.Object value, int startInd...
Insert Method void Insert(int index, System.Object value), void IList.Insert(int index, Syste...
InsertRange Method void InsertRange(int index, System.Collections.ICollection c)
LastIndexOf Method int LastIndexOf(System.Object value), int LastIndexOf(System.Object value, int ...
Remove Method void Remove(System.Object obj), void IList.Remove(System.Object value)
RemoveAt Method void RemoveAt(int index), void IList.RemoveAt(int index)
RemoveRange Method void RemoveRange(int index, int count)
Reverse Method void Reverse(), void Reverse(int index, int count)
SetRange Method void SetRange(int index, System.Collections.ICollection c)
Sort Method void Sort(), void Sort(System.Collections.IComparer comparer), void Sort(int in...
ToArray Method System.Object[] ToArray(), array ToArray(type type)
ToString Method string ToString()
TrimToSize Method void TrimToSize()
Item ParameterizedProperty System.Object Item(int index) {get;set;}
Capacity Property int Capacity {get;set;}
Count Property int Count {get;}
IsFixedSize Property bool IsFixedSize {get;}
IsReadOnly Property bool IsReadOnly {get;}
IsSynchronized Property bool IsSynchronized {get;}
SyncRoot Property System.Object SyncRoot {get;}
Como se puede ver, hay muchos métodos que no tiene la clase System.Object[], por ejemplos tiene método Sort y lo que es aún más importante, tiene método Remove.
Nombre |
Descripción |
||
Obtiene o establece el número de elementos que puede contener el objeto ArrayList. |
|||
Obtiene el número de elementos contenido realmente en ArrayList. |
|||
Obtiene un valor que indica si la clase ArrayList tiene un tamaño fijo. |
|||
Obtiene un valor que indica si la clase ArrayList es de sólo lectura. |
|||
Obtiene un valor que indica si el acceso a ArrayList está sincronizado (es seguro para la ejecución de subprocesos). |
|||
Obtiene o establece el elemento que se encuentra en el índice especificado. |
|||
Obtiene un objeto que se puede utilizar para sincronizar el acceso a la clase ArrayList. |
|||
Estos son los métodos de la clase ArrayList:
|
Nombre |
Descripción |
|
Crea un contenedor de ArrayList para una interfaz IList concreta. |
|||
Agrega un objeto al final de ArrayList. |
|||
Agrega los elementos de ICollection al final de ArrayList. |
|||
Sobrecargado. Utiliza un algoritmo de búsqueda binaria para localizar un elemento concreto en la ArrayList ordenada o en una parte de ella. |
|||
Quita todos los elementos de la clase ArrayList. |
|||
Crea una copia superficial del objeto ArrayList. |
|||
Determina si un elemento se encuentra en la clase ArrayList. |
|||
Sobrecargado. Copia la ArrayList o una parte de la misma en una matriz unidimensional. |
|||
Sobrecargado. Determina si dos instancias de Object son iguales. (Se hereda de Object). |
|||
Sobrecargado. Devuelve un contenedor de lista con un tamaño fijo, donde se permite modificar elementos pero no agregarlos ni quitarlos. |
|||
Sobrecargado. Devuelve un enumerador que recorre en iteración la colección de objetos ArrayList. |
|||
Sirve como función hash para un tipo concreto. GetHashCode es apropiado para su utilización en algoritmos de hash y en estructuras de datos como las tablas hash. (Se hereda de Object). |
|||
Devuelve una ArrayList que representa un subconjunto de los elementos de la ArrayList de origen. |
|||
Obtiene el objeto Type de la instancia actual. (Se hereda de Object). |
|||
Sobrecargado. Devuelve el índice de base cero de la primera aparición de un valor en la ArrayList o en una parte de ella. |
|||
Inserta un elemento en la clase ArrayList en el índice especificado. |
|||
Inserta los elementos de una colección en ArrayList en el índice especificado. |
|||
Sobrecargado. Devuelve el índice de base cero de la última aparición de un valor en la ArrayList o en una parte de ella. |
|||
Sobrecargado. Devuelve un contenedor de lista de sólo lectura. |
|||
Determina si las instancias de Object especificadas son la misma instancia. (Se hereda de Object). |
|||
Quita la primera aparición de un objeto concreto de ArrayList. |
|||
Quita el elemento en el índice especificado de ArrayList. |
|||
Quita todos los elementos de ArrayList. |
|||
Devuelve una ArrayList cuyos elementos son copias del valor especificado. |
|||
Sobrecargado. Invierte el orden de los elementos en la ArrayList o en una parte de ella. |
|||
Copia los elementos de una colección en un intervalo de elementos de ArrayList. |
|||
Sobrecargado. Ordena los elementos en la ArrayList o en una parte de ella. |
|||
Sobrecargado. Devuelve un contenedor de lista que está sincronizado (es seguro para subprocesos). |
|||
Sobrecargado. Copia los elementos de ArrayList en una nueva matriz. |
|||
Devuelve una clase String que representa la clase Object actual. (Se hereda de Object). |
|||
Establece la capacidad en el número de elementos real de la colección de objetos ArrayList. |
|||
Esta es quizás la parte más interesante de un ArrayList que hace su uso deseable cuando necesitamos que a un array se le quiten elementos. Podemos quitar los elementos de tres formas, de uno en uno en función de su contenido, de uno en uno en función de su índice o en rangos.
Primero veremos cómo quitarlos de uno en uno en función de su contenido. Esto se realiza con el método Remove:
PS> $ArrayList
1
2
3
4
5
PS> $ArrayList.Remove(3)
PS> $ArrayList
1
2
4
5
Como podemos observar, se ha quitado el elemento que tenía de índice 2 y cuyo contenido es el que se pasó como parámetro a Remove. ¿Qué pasa si el valor está en más de un elemento?:
PS> [System.Collections.ArrayList] $ArrayList = 1,2,3,4,3,5
PS> $ArrayList
1
2
3
4
3
5
PS> $ArrayList.Remove(3)
PS> $ArrayList
1
2
4
3
5
Como podemos ver, en este caso se ha quitado el primer elemento cuyo contenido es el recibido como parámetro, pero no el siguiente elemento de igual contenido. Este comportamiento es igual con cadenas:
PS> [System.Collections.ArrayList] $ArrayList = "a","b","c","d","e"
PS> $ArrayList.Remove("c")
PS> $ArrayList
a
b
d
e
PS> [System.Collections.ArrayList] $ArrayList = "a","b","c","d","c","e"
PS> $ArrayList.Remove("c")
PS> $ArrayList
a
b
d
c
e
Si lo que queremos es quitar un elemento en función a su índice el método a usar es RemoveAt:
PS> [System.Collections.ArrayList] $ArrayList = 1,2,3,4,3,5
PS> $ArrayList
1
2
3
4
3
5
PS> [System.Collections.ArrayList] $ArrayList = 1,2,3,4,3,5
PS> $ArrayList.RemoveAt(2)
PS> $ArrayList
1
2
4
3
5
PS> $ArrayList[2]
4
Como se puede ver, cuando se elimina un elemento, el resto de elementos posteriores se desplazan para ocupar el sitio vacante; en el ejemplo se elimina el elemento de índice 2 (se trata del primer 3) y se comprueba como el elemento siguiente, el cuatro, pasa a tener el índice 2.
Por último, para eliminar un rango de elementos se usa el método RemoveRange, que recibe dos parámetros: el primero es el índice del primer elemento que se eliminará y el segundo el número de elementos a eliminar:
PS> [System.Collections.ArrayList] $ArrayList = 1,2,3,4,3,5
PS> $ArrayList.RemoveRange(2,2)
PS> $ArrayList
1
2
3
5
PS> $ArrayList[2]
3
En el ejemplo se elimina dos elementos, cuyo primer índice es el 2. Al igual que pasaba con el método RemoveAt, los elementos posteriores se desplazan para ocupar los sitios vacantes.
Aquí termina este ladrillo, espero que pueda ser útil para alguien :o)