La salida de texto de los comandos en la ventana del intérprete de PowerShell es solo una forma de mostrar información en un formato comprensible para los humanos. En realidad, el entorno a trabajar con objetos: los cmdlets y funciones reciben objetos como entrada y , y los tipos de variables disponibles en modo interactivo y en scripts se basan en clases .NET. En el cuarto artículo de la serie, estudiaremos el trabajo con objetos con más detalle.
Contenido:
Objetos en PowerShell
Recordemos que un objeto es un conjunto de campos de datos (propiedades, eventos, etc.) y métodos para procesarlos. Su estructura está determinada por el tipo, que generalmente se basa en las clases utilizadas en la plataforma unificada .NET Core. También es posible trabajar con objetos COM, CIM (WMI) y ADSI. Las propiedades y métodos son necesarios para realizar diversas acciones sobre los datos. Además, en PowerShell se pueden pasar objetos como argumentos a funciones y cmdlets, asignar sus valores a variables, y también existe (pipeline). Cada comando en el pipeline pasa su salida al siguiente de forma secuencial — objeto por objeto. Para el procesamiento se pueden usar cmdlets compilados o crear funciones , para realizar diversas manipulaciones con objetos en el pipeline: filtrado, ordenación, agrupación e incluso modificación de su estructura. Transmitir datos de esta manera tiene una ventaja significativa: el comando receptor no necesita realizar un análisis sintáctico del flujo de bytes (texto), toda la información necesaria se puede extraer fácilmente accediendo a las propiedades y métodos correspondientes.
Visualización de la estructura de objetos
Como ejemplo, ejecutemos el cmdlet Get-Process, que permite obtener información sobre los procesos que se están ejecutando en el sistema:

Mostrará ciertos datos de texto formateados que no dan idea de las propiedades de los objetos devueltos y sus métodos. Para un análisis más detallado de la salida, es necesario aprender a investigar la estructura de los objetos, y para ello nos ayudará el cmdlet Get-Member:
Get-Process | Get-Member 
Aquí ya vemos el tipo y la estructura, y con parámetros adicionales podemos, por ejemplo, mostrar solo las propiedades del objeto que ha entrado:
Get-Process | Get-Member -MemberType PropertyEstos conocimientos serán útiles para resolver tareas de administración en modo interactivo o para escribir scripts propios: digamos, para obtener información sobre procesos colgados mediante la propiedad Responding.
Filtrado de objetos
PowerShell permite pasar por el pipeline objetos que cumplan con una condición específica:
Where-Object { bloque de script }El resultado de la ejecución del bloque de script entre llaves debe ser un valor booleano. Si es verdadero ($true), el objeto que entró al cmdlet Where-Object se pasará al siguiente en el pipeline; de lo contrario (valor $false) se eliminará. Como ejemplo, mostraremos una lista de servicios detenidos en Windows Server, es decir, aquellos cuyo estado tiene el valor "Stopped":
Get-Service | Where-Object {$_.Status -eq "Stopped"} 
Aquí de nuevo vemos una representación textual, pero si se desea entender el tipo y la estructura interna de los objetos que pasan por el pipeline, no es difícil:
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Get-Member 
Ordenación de objetos
En el procesamiento por pipeline de objetos, a menudo surge la necesidad de ordenarlos. Al cmdlet Sort-Object se le pasan los nombres de las propiedades (claves de ordenación), y este devuelve los objetos ordenados por sus valores. La salida de los procesos en ejecución se puede ordenar fácilmente por el tiempo de CPU consumido (propiedad cpu):
Get-Process | Sort-Object –Property cpuNo es necesario especificar el parámetro -Property al invocar el cmdlet Sort-Object, ya que se usa por defecto. Para la ordenación inversa se utiliza el parámetro -Descending:
Get-Process | Sort-Object cpu -Descending 
Selección de objetos y sus partes
El cmdlet Select-Object permite seleccionar un número determinado de objetos al principio o al final del pipeline mediante los parámetros -First o -Last. Con él se pueden elegir objetos individuales o propiedades específicas, así como crear nuevos objetos basados en ellos. Analicemos el funcionamiento del cmdlet con ejemplos sencillos.
El siguiente comando muestra información sobre los 10 procesos que consumen la mayor cantidad de memoria RAM (propiedad WS):
Get-Process | Sort-Object WS -Descending | Select-Object -First 10 
Se pueden seleccionar solo ciertas propiedades de los objetos que pasan a través del canal y crear nuevos en función de ellas:
Get-Process | Select-Object ProcessName, Id -First 1Como resultado del canal obtendremos un nuevo objeto cuya estructura será diferente de la estructura devuelta por el comando Get-Process. Verifiquémoslo con Get-Member:
Get-Process | Select-Object ProcessName, Id -First 1 | Get-Member 
Tenga en cuenta que Select-Object devuelve un único objeto (-First 1), el cual solo tiene dos campos que especificamos: sus valores fueron copiados del primer objeto pasado al canal por el comando Get-Process. Uno de los métodos para crear objetos en scripts de PowerShell se basa en el uso de Select-Object:
$obj = Get-Process | Select-Object ProcessName, Id -First 1
$obj.GetType() 
Con Select-Object se pueden agregar propiedades calculadas a los objetos que deben presentarse como . En este caso, el valor de su primera clave corresponde al nombre de la propiedad, y el valor de la segunda es el valor de la propiedad para el elemento actual del canal:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} 
Veamos la estructura de los objetos que pasan a través del canal:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} | Get-Member 
ForEach-Object, Group-Object y Measure-Object
Existen otros comandos para trabajar con objetos. Por ejemplo, mencionaremos tres de los más útiles:
ForEach-Object permite ejecutar código en PowerShell para cada objeto en el canal:
ForEach-Object { bloque de script }Group-Object agrupa objetos por el valor de una propiedad:
Group-Object PropertyNameSi se ejecuta con el parámetro -NoElement, se puede conocer la cantidad de elementos en los grupos.
Measure-Object agrega varios parámetros resumidos según los valores de los campos de los objetos en el canal (calcula la suma, así como encuentra el valor mínimo, máximo o promedio):
Measure-Object -Property PropertyName -Minimum -Maximum -Average -SumPor lo general, los comandos mencionados se utilizan en modo interactivo, mientras que en scripts con frecuencia se crean con bloques Begin, Process y End.
Creación de objetos .NET y COM (New-Object)
Hay muchos componentes de software con interfaces .NET Core y COM que son útiles para los administradores de sistemas. Con la clase System.Diagnostics.EventLog se pueden gestionar los registros del sistema directamente desde Windows PowerShell. Vamos a ver un ejemplo de cómo crear una instancia de esta clase utilizando el cmdlet New-Object con el parámetro -TypeName:
New-Object -TypeName System.Diagnostics.EventLog 
Dado que no hemos especificado un registro de eventos en particular, la instancia de la clase obtenida no contiene datos. Para cambiar esto, es necesario llamar a un método constructor especial durante su creación utilizando el parámetro -ArgumentList. Si queremos acceder al registro de aplicaciones, tendremos que pasar la cadena «Application» como argumento al constructor:
$AppLog = New-Object -TypeName System.Diagnostics.EventLog -ArgumentList Application
$AppLog 
Nota: hemos guardado la salida del comando en la variable $AppLog. Aunque normalmente se utilizan tuberías en el modo interactivo, a menudo la escritura de scripts requiere guardar una referencia al objeto. Además, las clases básicas de .NET Core se encuentran en el espacio de nombres System: PowerShell busca esos tipos por defecto allí, por lo que escribir Diagnostics.EventLog en lugar de System.Diagnostics.EventLog es completamente correcto.
Para trabajar con el registro, se pueden llamar a los métodos correspondientes:
$AppLog | Get-Member -MemberType Method 
Digamos que se limpia con el método Clear() si hay privilegios de acceso:
$AppLog.Clear()El cmdlet New-Object también se aplica para trabajar con componentes COM. Hay muchos, desde las bibliotecas que se entregan con el servidor de scripts de Windows hasta aplicaciones ActiveX, como, por ejemplo, Internet Explorer. Para crear un objeto COM, se requiere especificar el parámetro -ComObject con el identificador de programa ProgId de la clase deseada:
New-Object -ComObject WScript.Shell
New-Object -ComObject WScript.Network
New-Object -ComObject Scripting.Dictionary
New-Object -ComObject Scripting.FileSystemObjectPara crear objetos propios con una estructura arbitraria, el uso de New-Object parece demasiado anticuado y engorroso; este cmdlet se utiliza para trabajar con componentes de software externos a PowerShell. En los próximos artículos, este tema será tratado con más detalle. Además de los objetos .NET y COM, también estudiaremos objetos CIM (WMI) y ADSI.
Llamada a métodos estáticos
No es posible crear instancias de algunas clases de .NET Core: entre ellas se encuentran System.Environment y System.Math. Son y contienen solo propiedades y métodos estáticos. En esencia, son bibliotecas de referencia que se utilizan sin crear objetos. Se puede hacer referencia a una clase estática mediante un literal, encerrando el nombre del tipo entre corchetes. Al mirar la estructura del objeto con Get-Member, veremos el tipo System.RuntimeType en lugar de System.Environment:
[System.Environment] | Get-Member 
Para ver solo los elementos estáticos, debe llamar a Get-Member con el parámetro -Static (note el tipo de objeto):
[System.Environment] | Get-Member -Static 
Para acceder a las propiedades y métodos estáticos, se utilizan dos dos puntos consecutivos en lugar de un punto después del literal:
[System.Environment]::OSVersionO
$test=[System.Math]::Sqrt(25)
$test
$test.GetType() 
Tipo PSCustomObject
Entre los numerosos tipos de datos disponibles en PowerShell, se destaca PSCustomObject, destinado a almacenar objetos con una estructura arbitraria. La creación de tal objeto mediante el comando New-Object se considera un método clásico, pero engorroso y anticuado:
$object = New-Object –TypeName PSCustomObject -Property @{Name = 'Ivan Danko';
City = 'Moscú';
Country = 'Rusia'}
Veamos la estructura del objeto:
$object | Get-Member 
A partir de PowerShell 3.0 también está disponible otra sintaxis:
$object = [PSCustomObject]@{Name = 'Ivan Danko';
City = 'Moscú';
Country = 'Rusia'
}
Se puede acceder a los datos de una de las maneras equivalentes:
$object.Name
$object.'Name'
$value = 'Name'
$object.$value
Veamos un ejemplo de conversión en un objeto de un hash existente:
$hash = @{'Name'='Ivan Danko'; 'City'='Moscú'; 'Country'='Rusia'}
$hash.GetType()
$object = [pscustomobject]$hash
$object.GetType()

Una de las desventajas de los objetos de este tipo es que el orden de sus propiedades puede cambiar. Para evitar esto, es necesario usar el atributo [ordered]:
$object = [PSCustomObject][ordered]@{Name = 'Ivan Danko';
City = 'Moscú';
Country = 'Rusia'
}
También hay otras formas de crear un objeto: arriba hemos revisado el uso del comando . Solo queda aclarar cómo añadir y eliminar elementos. Hacer esto para el objeto del ejemplo anterior es bastante sencillo:
$object | Add-Member –MemberType NoteProperty –Name Age –Value 33
$object | Get-Member

El comando Add-Member permite añadir no solo propiedades, sino también métodos al objeto $object utilizando la construcción "-MemberType ScriptMethod":
$ScriptBlock = {
# código
}
$object | Add-Member -Name "MyMethod" -MemberType ScriptMethod -Value $ScriptBlock
$object | Get-Member
Tenga en cuenta: para almacenar el código del nuevo método, hemos utilizado la variable $ScriptBlock de tipo ScriptBlock.

Para eliminar propiedades, se utiliza el método correspondiente:
$object.psobject.properties.remove('Name')
Creación de clases propias
En PowerShell 5.0 se introdujo la posibilidad de definir utilizando la sintaxis característica de lenguajes de programación orientados a objetos. Para ello, se utiliza la palabra clave Class, después de la cual se debe especificar el nombre de la clase y describir su cuerpo entre corchetes:
class MyClass
{
# cuerpo de la clase
}
Este es un verdadero tipo de .NET Core, cuyo cuerpo describe sus propiedades, métodos y otros elementos. Examinemos un ejemplo de definición de una clase simple:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
}
Para crear un objeto (instancia de clase) se utiliza el cmdlet , o el literal de tipo [MyClass] y new (constructor por defecto):
$object = New-Object -TypeName MyClasso
$object = [MyClass]::new()Analicemos la estructura del objeto:
$object | Get-Member 
No olvide el alcance: no se puede hacer referencia al nombre del tipo como una cadena o utilizar el literal de tipo fuera del script o módulo en el que se definió la clase. Sin embargo, las funciones pueden devolver instancias de clase (objetos) que estarán disponibles fuera del módulo o script.
Después de crear el objeto, llenemos sus propiedades:
$object.Name = 'Ivan Danko'
$object.City = 'Moscú'
$object.Country = 'Rusia'
$object

Cabe destacar que en la descripción de la clase se especifican no solo los tipos de propiedades, sino también sus valores por defecto:
class Example
{
[string]$Name = 'John Doe'
}
La descripción del método de la clase es similar a la descripción de una función, pero sin utilizar la palabra clave function. Al igual que en la función, se pueden pasar parámetros a los métodos si es necesario:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
# descripción del método
Smile([bool]$param1)
{
If($param1) {
Write-Host ':)'
}
}
}
Ahora un representante de nuestra clase puede sonreír:
$object = [MyClass]::new()
$object.Smile($true)
Los métodos se pueden sobrecargar, además, la clase tiene , así como constructores cuyos nombres coinciden con el nombre de la clase misma. Una clase definida en un script o módulo de PowerShell puede servir de base para otra: así se implementa la herencia. Se permite el uso de clases existentes de .NET como clases base:
class MyClass2 : MyClass
{
#cuerpo de la nueva clase, que tiene como base MyClass
}
[MyClass2]::new().Smile($true)
Nuestra descripción del trabajo con objetos en PowerShell difícilmente puede considerarse exhaustiva. En próximas publicaciones intentaremos profundizar en ella con ejemplos prácticos: el quinto artículo del ciclo estará dedicado a la integración de PowerShell con componentes de software externos. Las partes anteriores se pueden encontrar en los enlaces de abajo.
Fuente: habr.com
