The text output of commands in the PowerShell interpreter window is just a way to display information in a human-readable form. Actually Wednesday to work with objects: cmdlets and functions receive them as input and , and the variable types available interactively and in scripts are based on .NET classes. In the fourth article of the series, we will study working with objects in more detail.
Table of Contents:
Objects in PowerShell
Recall that an object is a collection of data fields (properties, events, etc.) and ways of processing them (methods). Its structure is given by a type, which is typically based on the classes used in the .NET Core unified platform. It is also possible to work with COM, CIM (WMI) and ADSI objects. Properties and methods are needed to perform various actions on data, in addition, in PowerShell, objects can be passed as arguments to functions and cmdlets, assign their values to variables, and there is also (conveyor or pipeline). Each command in the pipeline passes its output to the next in turn, object by object. For processing, you can use compiled cmdlets or create your own to perform various manipulations with objects in the pipeline: filtering, sorting, grouping, and even changing their structure. Transferring data in this form has a significant advantage: the receiving team does not need to parse the stream of bytes (text), all the necessary information is easily retrieved by accessing the appropriate properties and methods.
Viewing the Structure of Objects
For example, let's run the Get-Process cmdlet, which allows you to get information about the processes running in the system:

It will display some formatted text data that does not give an idea about the properties of the returned objects and their methods. To fine-tune the output, you need to learn how to examine the structure of objects, and the Get-Member cmdlet will help us with this:
Get-Process | Get-Member 
Here we already see the type and structure, and with the help of additional parameters we can, for example, display only the properties of the object that got to the input:
Get-Process | Get-Member -MemberType PropertyYou will need this knowledge to solve administrative tasks interactively or to write your own scripts: for example, to get information about hung processes by the Responding property.
Object filtering
PowerShell allows you to pipe objects that meet a certain condition:
Where-Object { блок сценария }The result of executing a script block within operator brackets must be a boolean value. If it is true ($true), the object that got into the input of the Where-Object cmdlet will be passed down the pipeline, otherwise (value $false) it will be deleted. For example, let's display a list of stopped Windows Server services, i.e. those with the Status property set to "Stopped":
Get-Service | Where-Object {$_.Status -eq "Stopped"} 
Here again we see a textual representation, but if you want to understand the type and internal structure of the objects passing through the pipeline, it is not difficult:
Get-Service | Where-Object {$_.Status -eq "Stopped"} | Get-Member 
Sorting objects
When objects are pipelined, it is often necessary to sort them. The Sort-Object cmdlet is passed the names of the properties (sort keys) and returns the objects sorted by their values. The output of running processes can be easily sorted by CPU time spent (cpu property):
Get-Process | Sort-Object –Property cpuThe -Property parameter can be omitted when calling the Sort-Object cmdlet and is used by default. For reverse sorting, the -Descending parameter is used:
Get-Process | Sort-Object cpu -Descending 
Selection of objects and their parts
The Select-Object cmdlet allows you to select a specific number of objects at the beginning or end of a pipeline using the -First or -Last options. With it, you can select single objects or specific properties, as well as create new objects based on them. Let's analyze the work of the cmdlet using simple examples.
The following command displays information about the 10 processes consuming the maximum amount of RAM (WS property):
Get-Process | Sort-Object WS -Descending | Select-Object -First 10 
You can select only certain properties of objects passing through the pipeline and create new ones based on them:
Get-Process | Select-Object ProcessName, Id -First 1As a result of the pipeline, we will get a new object, the structure of which will differ from the structure returned by the Get-Process cmdlet. Verify this with Get-Member:
Get-Process | Select-Object ProcessName, Id -First 1 | Get-Member 
Note that Select-Object returns a single object (-First 1) that has only the two fields we specified: their values were copied from the first object passed into the pipeline by the Get-Process cmdlet. One of the ways to create objects in PowerShell scripts is based on the use of Select-Object:
$obj = Get-Process | Select-Object ProcessName, Id -First 1
$obj.GetType() 
With Select-Object, you can add computed properties to objects that you want to represent as . In this case, the value of its first key corresponds to the property name, and the value of the second key corresponds to the property value for the current pipeline element:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} 
Let's look at the structure of the objects passing through the pipeline:
Get-Process | Select-Object -Property ProcessName, @{Name="StartTime"; Expression = {$_.StartTime.Minute}} | Get-Member 
ForEach-Object, Group-Object and Measure-Object
There are other cmdlets for working with objects. For example, let's talk about the three most useful:
ForEach-Object allows you to execute PowerShell code for each object in the pipeline:
ForEach-Object { блок сценария }Group-Object groups objects by property value:
Group-Object PropertyNameIf you run it with the -NoElement parameter, you can find out the number of elements in groups.
Measure-Object aggregates various summary parameters by the values of the fields of objects in the pipeline (calculates the sum, and also finds the minimum, maximum, or average value):
Measure-Object -Property PropertyName -Minimum -Maximum -Average -SumTypically, the discussed cmdlets are used in interactive mode, and scripts are more often created with Begin, Process and End blocks.
Creating .NET and COM Objects (New-Object)
There are many software components with .NET Core and COM interfaces that are useful to system administrators. With the System.Diagnostics.EventLog class, you can manage system logs directly from Windows PowerShell. Let's look at an example of creating an instance of this class using the New-Object cmdlet with the -TypeName parameter:
New-Object -TypeName System.Diagnostics.EventLog 
Because we didn't specify a specific event log, the resulting class instance contains no data. To change this, you need to call a special constructor method during its creation using the -ArgumentList parameter. If we want to access the application log, the string "Application" should be passed to the constructor as an argument:
$AppLog = New-Object -TypeName System.Diagnostics.EventLog -ArgumentList Application
$AppLog 
Please note that we saved the output of the command in the $AppLog variable. While pipelines are commonly used interactively, scripting often requires maintaining a reference to an object. Also, the core .NET Core classes are contained in the System: namespace by default. PowerShell looks for the specified types in it, so writing Diagnostics.EventLog instead of System.Diagnostics.EventLog is fine.
To work with the log, you can refer to the corresponding methods:
$AppLog | Get-Member -MemberType Method 
Let's say it is cleared by the Clear() method if there are access rights:
$AppLog.Clear()The New-Object cmdlet is also used to work with COM components. There are quite a few of them, from the libraries that come with Windows Script Server to ActiveX applications such as Internet Explorer. To create a COM object, you need to specify the -ComObject parameter with the programmatic ProgId of the desired class:
New-Object -ComObject WScript.Shell
New-Object -ComObject WScript.Network
New-Object -ComObject Scripting.Dictionary
New-Object -ComObject Scripting.FileSystemObjectTo create your own objects with an arbitrary structure, using New-Object looks too archaic and cumbersome, this cmdlet is used to work with program components external to PowerShell. Future articles will address this issue in more detail. In addition to .NET and COM objects, we will also explore CIM (WMI) and ADSI objects.
Calling static methods
Some .NET Core classes cannot be instantiated, including System.Environment and System.Math. They are and contain only static properties and methods. In essence, these are reference libraries that are used without creating objects. You can refer to a static class as a literal by enclosing the type name in square brackets. At the same time, if we look at the structure of the object using Get-Member, we will see the System.RuntimeType type instead of System.Environment:
[System.Environment] | Get-Member 
To view only static members, you need to call Get-Member with the -Static parameter (pay attention to the type of the object):
[System.Environment] | Get-Member -Static 
To access static properties and methods, two consecutive colons are used instead of a dot after the literal:
[System.Environment]::OSVersionOr
$test=[System.Math]::Sqrt(25)
$test
$test.GetType() 
Type PSCustomObject
Among the numerous data types available in PowerShell, it is worth mentioning PSCustomObject, which is designed to store objects with an arbitrary structure. Creating such an object using the New-Object cmdlet is considered a classic, but cumbersome and outdated way:
$object = New-Object –TypeName PSCustomObject -Property @{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'}
Let's look at the structure of the object:
$object | Get-Member 
Starting with PowerShell 3.0, another syntax is available:
$object = [PSCustomObject]@{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'
}
You can access the data in one of the equivalent ways:
$object.Name
$object.'Name'
$value = 'Name'
$object.$value
Here is an example of converting an existing hashtable into an object:
$hash = @{'Name'='Ivan Danko'; 'City'='Moscow'; 'Country'='Russia'}
$hash.GetType()
$object = [pscustomobject]$hash
$object.GetType()

One of the disadvantages of objects of this type is that the order of their properties can change. To avoid this, you need to use the [ordered] attribute:
$object = [PSCustomObject][ordered]@{Name = 'Ivan Danko';
City = 'Moscow';
Country = 'Russia'
}
There are other options for creating an object: above, we looked at using the cmdlet . It remains to deal with adding and removing elements. Doing this for the object from the previous example is quite simple:
$object | Add-Member –MemberType NoteProperty –Name Age –Value 33
$object | Get-Member

The Add-Member cmdlet allows you to add not only properties, but also methods to a previously created $object by using the "-MemberType ScriptMethod" construct:
$ScriptBlock = {
# код
}
$object | Add-Member -Name "MyMethod" -MemberType ScriptMethod -Value $ScriptBlock
$object | Get-Member
Note that we used the $ScriptBlock variable of the ScriptBlock type to store the code for the new method.

To remove properties, use the corresponding method:
$object.psobject.properties.remove('Name')
Creating Your Own Classes
PowerShell 5.0 introduced the ability to define using the syntax characteristic of object-oriented programming languages. The Class keyword is used for this, after which you should specify the class name and describe its body in operator brackets:
class MyClass
{
# тело класса
}
This is a true .NET Core type, and its body describes its properties, methods, and other elements. Consider an example of a simple class definition:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
}
To create an object (an instance of a class), use the cmdlet , or a literal of type [MyClass] and new (default constructor):
$object = New-Object -TypeName MyClassor
$object = [MyClass]::new()Let's analyze the structure of the object:
$object | Get-Member 
Don't forget about scope: you can't refer to a type name as a string or use a type literal outside of the script or module in which the class is defined. At the same time, functions can return class instances (objects) that will be available outside the module or script.
After creating the object, fill in its properties:
$object.Name = 'Ivan Danko'
$object.City = 'Moscow'
$object.Country = 'Russia'
$object

Note that in the class description, not only the types of properties are specified, but also their default values:
class Example
{
[string]$Name = 'John Doe'
}
The description of a class method resembles a description of a function, but without the function word. As in functions, parameters are passed to methods if necessary:
class MyClass
{
[string]$Name
[string]$City
[string]$Country
#описание метода
Smile([bool]$param1)
{
If($param1) {
Write-Host ':)'
}
}
}
Now the representative of our class knows how to smile:
$object = [MyClass]::new()
$object.Smile($true)
Methods can be overloaded, in addition, the class has , as well as constructors whose names match the name of the class itself. A class defined in a script or a PowerShell module can serve as a base class for another - this is how inheritance is implemented. At the same time, existing .NET classes can be used as base ones:
class MyClass2 : MyClass
{
#тело нового класса, базовым для которого является MyClass
}
[MyClass2]::new().Smile($true)
Our description of working with objects in PowerShell is hardly exhaustive. In the following publications, we will try to deepen it with practical examples: the fifth article of the series will be devoted to integration of PowerShell with third-party software components. Past parts can be found at the links below.
Source: habr.com
