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

 

¿Cómo se definen arrays en Powershell?

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

 

¿Cómo se definen matrices?

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

 

¿Cómo accedo a los elementos de un array y cómo los modifico? ¿Y cuántos elementos contiene?

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.

¿Cuántos elementos tiene el array o la matriz?

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.

¿Cómo accedo al array o a la matriz al completo?

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

¿Cómo leo o modifico un elemento de un array?

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

¿Cómo leo o modifico una matriz?

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

¿Cómo accedo a varios elementos de un array a la vez?

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

¿Cuál es el tipo de datos del array?

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.

¿Cómo agrego elementos y qué pasa con el tipo del array cuando se agregan elementos?

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

¿Puedo quitar elementos de un array?

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[]

¿Se pueden combinar los array?

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

¿Se puede ordenar un array?

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.

La clase ArrayList

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.

Definición de un ArrayList

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

Miembros de la clase 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.

Propiedades de ArrayList


 

Nombre

Descripción

Capacity

Obtiene o establece el número de elementos que puede contener el objeto ArrayList.

Count

Obtiene el número de elementos contenido realmente en ArrayList.

IsFixedSize

Obtiene un valor que indica si la clase ArrayList tiene un tamaño fijo.

IsReadOnly

Obtiene un valor que indica si la clase ArrayList es de sólo lectura.

IsSynchronized

Obtiene un valor que indica si el acceso a ArrayList está sincronizado (es seguro para la ejecución de subprocesos).

Item

Obtiene o establece el elemento que se encuentra en el índice especificado.

SyncRoot

Obtiene un objeto que se puede utilizar para sincronizar el acceso a la clase ArrayList.

 

Métodos de ArrayList

Estos son los métodos de la clase ArrayList:

 

 

Nombre

Descripción

Adapter

Crea un contenedor de ArrayList para una interfaz IList concreta.

Add

Agrega un objeto al final de ArrayList.

AddRange

Agrega los elementos de ICollection al final de ArrayList.

BinarySearch

Sobrecargado. Utiliza un algoritmo de búsqueda binaria para localizar un elemento concreto en la ArrayList ordenada o en una parte de ella.

Clear

Quita todos los elementos de la clase ArrayList.

Clone

Crea una copia superficial del objeto ArrayList.

Contains

Determina si un elemento se encuentra en la clase ArrayList.

CopyTo

Sobrecargado. Copia la ArrayList o una parte de la misma en una matriz unidimensional.

Equals 

Sobrecargado. Determina si dos instancias de Object son iguales. (Se hereda de Object).

FixedSize

Sobrecargado. Devuelve un contenedor de lista con un tamaño fijo, donde se permite modificar elementos pero no agregarlos ni quitarlos.

GetEnumerator

Sobrecargado. Devuelve un enumerador que recorre en iteración la colección de objetos ArrayList.

GetHashCode 

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).

GetRange

Devuelve una ArrayList que representa un subconjunto de los elementos de la ArrayList de origen.

GetType 

Obtiene el objeto Type de la instancia actual. (Se hereda de Object).

IndexOf

Sobrecargado. Devuelve el índice de base cero de la primera aparición de un valor en la ArrayList o en una parte de ella.

Insert

Inserta un elemento en la clase ArrayList en el índice especificado.

InsertRange

Inserta los elementos de una colección en ArrayList en el índice especificado.

LastIndexOf

Sobrecargado. Devuelve el índice de base cero de la última aparición de un valor en la ArrayList o en una parte de ella.

ReadOnly

Sobrecargado. Devuelve un contenedor de lista de sólo lectura.

ReferenceEquals 

Determina si las instancias de Object especificadas son la misma instancia. (Se hereda de Object).

Remove

Quita la primera aparición de un objeto concreto de ArrayList.

RemoveAt

Quita el elemento en el índice especificado de ArrayList.

RemoveRange

Quita todos los elementos de ArrayList.

Repeat

Devuelve una ArrayList cuyos elementos son copias del valor especificado.

Reverse

Sobrecargado. Invierte el orden de los elementos en la ArrayList o en una parte de ella.

SetRange

Copia los elementos de una colección en un intervalo de elementos de ArrayList.

Sort

Sobrecargado. Ordena los elementos en la ArrayList o en una parte de ella.

Synchronized

Sobrecargado. Devuelve un contenedor de lista que está sincronizado (es seguro para subprocesos).

ToArray

Sobrecargado. Copia los elementos de ArrayList en una nueva matriz.

ToString 

Devuelve una clase String que representa la clase Object actual. (Se hereda de Object).

TrimToSize

Establece la capacidad en el número de elementos real de la colección de objetos ArrayList.

Quitar elementos de un 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)