Using an interactive shell
Windows PowerShell can be used either interactively or as a script automation. Before we take a look at script automations, let's explore the use of an interactive shell.
We can do arithmetic operations in Windows PowerShell as follows:
2 + 2
This will return 4
. Consider the following operation:
2 + 2 * 3
This will return 8
.
Windows PowerShell does left-to-right arithmetic operations according to the precedence rules. In the preceding expressions, PowerShell performed the operation as 2 * 3 + 2
. We need to use parenthesis, as with (2 + 2) * 3
, which return the result as 12
. Here, (2 + 2)
is known as grouping.
Using Windows PowerShell, we can do much more, such as system unit calculations, hexadecimal conversions, and so on, and few examples are as follows:
4GB / 1MB
This returns 4096
.
(0xc85).ToChar($_)
This returns the following character:
'ಅ'
In Windows PowerShell, we can use .NET classes. Here's an example:
[System.Math]::PI
This returns the value of pi, which is 3.14159265358979
.
Windows PowerShell allows us to use class directly, without using the namespace, shown as follows:
[Math]::PI
Note
Refer to the following MSDN link for the System.Math
class: https://msdn.microsoft.com/en-us/library/system.math%28v=vs.110%29.aspx
The command shown before was just an example. With reference to this, we can build code as per our needs using .NET classes.
Windows PowerShell cmdlets
In this topic, we will cover the following:
- Exploring Windows PowerShell commands
- Exploring Windows PowerShell modules
- Getting help
- Understanding aliases, expressions, objects, pipelines, filtering, and formatting
- Scripting with Windows PowerShell
Windows PowerShell is designed to execute four kinds of named commands:
- Cmdlets: These are .NET programs designed to interact with PowerShell
- Scripts: These are files with the
FileName.PS1
extension - Functions: These are a block of code that are very helpful for script organization
- Native window commands: These are internal commands such as MKDIR, CHDIR, and so on
The internal commands of Windows PowerShell are called cmdlets. Cmdlets are always named in the verb-and-noun combination. This appears as Verb-Noun, for example Get-Service
and Stop-Service,
where Get
and Stop
are verbs, and Service
is a noun.
(Verb)—Action
(Noun)—Something to be acted on
For example, Get-Service
Let's execute the following command:
Get-Command
The output of this command is illustrated in the following image:
The previous command will list all the installed commands from the computer and retrieve only the cmdlets, functions, and aliases.
To retrieve only cmdlets, we can use the CommandType
parameter, as shown in the following command:
Get-Command -CommandType Cmdlet
Note
Windows PowerShell supports tab completion. For example, let's run the following command:
Get-Comm
+ Tab + -Co
+ Tab + C
+ Tab
When we type out this command, it will result as follows:
Get-Command -CommandType Cmdlet
Now, let's explore the commands. In the following example, we will take a look at the cmdlet Get-Service
. Since we know the cmdlet up front, let's collect more information such as the cmdlet version and source, as follows:
Get-Command Get-Service
The output of the preceding command is illustrated in the following image:
The points marked in the figure are explained in the following list:
- 1: This is the type of the command
- 2: This is the name of the command
- 3: This indicates the version (From Windows PowerShell 5.0 onward)
- 4: This indicates the module's name
To retrieve the commands for a specific module, we need to use the Module
parameter, which accepts the module name (Source) as we saw in bullet 4. The following is an example of the command:
Get-Command -Module Microsoft.PowerShell.Management
This outputs all the commands available in the Microsoft.PowerShell.Management
module.
The Get-Service
command will retrieve all the services running on your local computer.
Note
Refer to the following link to get more information on the ServiceController
class:
https://msdn.microsoft.com/en-us/library/system.serviceprocess.servicecontroller%28v=vs.110%29.aspx
Let's see the alternate of the Get-Service
cmdlet using the .NET class. To do this, let's find the TypeName of the Get-Service
cmdlet by executing Get-Service
| Get-Member
and this gives the TypeName
as System.ServiceProcess.ServiceController
. Windows PowerShell allows us to use this directly as follows:
[System.ServiceProcess.ServiceController]::GetServices()
Now, let's take a look at how the command that we just discussed works.
Here, we will consume the .NET ServiceController
class in PowerShell and invoke the GetServices()
method.
The GetServices
method has an overload with a machineName
parameter. To find this, we will execute the following command:
[System.ServiceProcess.ServiceController]::GetServices
The difference is that here, we did not use parentheses. The output of OverloadDefinitions
is as follows:
OverloadDefinitions ------------------- static System.ServiceProcess.ServiceController[] GetServices() static System.ServiceProcess.ServiceController[] GetServices(string machineName)
We can query the service information of a remote computer by passing the host name of the remote computer as the machineName
parameter, as follows:
[System.ServiceProcess.ServiceController]::GetServices('RemoteServer')
Getting help
This is the most important and interesting topic. No matter what technology we consume, all we need to know is the way to get help for it. Most IT professionals and developers say that they use Google to find this.
For PowerShell, help is much more focused. It's very difficult to remember all the commands. So, we can search for a command and find help for the same. Let's take a look at how to seek help for Windows PowerShell cmdlets.
Get-Help Get-Service
The output is illustrated in the following image:
The sections in the image are explained as follows:
- NAME: This is the name of the command (
Get-Service
) - SYNOPSIS: This is the abstract of the command
- SYNTAX: This gives us the syntax of the commands, which includes all its parameters and its type
- DESCRIPTION: This is the description of the command whose help we are looking for
- RELATED LINKS: This contains the URL of online versions of the command, and other commands related to the one we are looking for help regarding
- REMARKS: This will guide us to explore examples, detailed information, full help, and online help
If more information than that fitting the page view is to be displayed, the console is paginated. For example, if we execute the Get-Help Get-Service -Detailed | more
command, the details will output as shown in the following image:
If we press Enter, we can view one line after another, whereas pressing the space key will give a page view.
Note
Keep your files updated using the Update-Help
cmdlet as shown in the following command. Ensure that your machine has internet connectivity and execute the following command:
Update-Help –Verbose
This cmdlet is designed to download and install help files on our computer.
The output is illustrated in the following image:
Ensure that you have an Internet connection while updating your help. The reason for updating help is to keep the help document up-to-date. Let us learn more about the Get-Help
cmdlet:
- The
Help
cmdlet is an alias forGet-Help
(Aliases will be covered in the next section). - The
Get-Help
cmdlet allows us to view the online help using theOnline
parameter. The following command will open the online URL in the default web browser:Get-Help Get-Service –Online
- The
Get-Help
cmdlet allows us to view the help content in a separate user interface. The following is the command that needs to be executed:Get-Help Get-Service –ShowWindow
The output of the preceding code is illustrated in the following image:
- To view only syntax, we can execute the following code:
(Get-Help Get-Service).Syntax
Understanding aliases
Using aliases in Windows PowerShell is not advised by many. The reason for this is readability and understandability. Developers are comfortable using an alias because it's easier, but the difficulty is in remembering the alias for each cmdlet.
The bottom line is that aliases are shortcuts for Windows PowerShell cmdlets.
Windows PowerShell has two types of aliases:
- The built-in alias: This is an alias that represents PowerShell's native cmdlets
- The user-defined alias: This is an alias created by us for specific needs.
The following command retrieves all the commands available in the Microsoft.PowerShell.Management
module:
Get-Command -Module Microsoft.PowerShell.Management
The following image shows the output of the previous command:
Using the following alias, we can achieve the same output as with –like
:
gcm -Module Microsoft.PowerShell.Management
Here, gcm
is an alias or shortcut for the Get-Command
cmdlet.
Let's explore all the commands related to an alias. The module name for aliases' commands is Microsoft.PowerShell.Utility
:
Get-Command -Name '*Alias*'
The output of this command is illustrated in the following image:
For now, ignore the PoshBoard
module; we will discuss modules in Chapter 2, Unleashing Development Skills Using Windows PowerShell 5.0.
To find an alias of any given command, we can simply execute the following code:
Get-Alias -Definition Get-Alias
The output of this command is as follows:
PS C:\> Get-Alias -Definition Get-Alias CommandType Name Version Source ----------- ---- ------- ------ Alias gal -> Get-Alias
Here, gal
is an alias of the Get-Alias
cmdlet. The Get-Alias
cmdlet will retrieve all the available aliases in your local computer.
PowerShell allows us to create aliases for any given valid cmdlet. To create a new alias, we execute the following command:
New-Alias -Name W -Value Get-WmiObject -Description "Learning PowerShell" -Verbose
The output of the command we just discussed is as follows:
VERBOSE: Performing the operation "New Alias" on target "Name: W Value: Get-WmiObject"
Now, let's take a look at how the command we just discussed works.
The New-Alias
command is used to create an alias.
The W
part is a friendly name. You can choose any name you need, but you can't create a new alias with the existing, used names.
The Value
command is used here. We also used the Get-WmiObject
command, and that's our definition.
The Description
parameter is for our reference.
The Verbose
parameter just shows the action performed by the message.
The output of the preceding command is illustrated in the following image:
The sections in the image are explained as follows:
- Name: This is the name of the alias.
- Definition: Here, this is the
Get-WmiObject
command. It may be any command. - Description: This is the description of the alias.
- CommandType: Here, this is
Alias
. - Visibility: Here, this is
Public
.
Using the Set-Alias
command, we can do the same, but it allows us to change the association later. In the preceding example, we created an alias named W
for the Get-WmiObject
command. If we try to create an alias with the same name for another command, the following error appears:
In this scenario, we can use the Set-Alias
command to change the alias' association. Run the following code:
PS C:\> Set-Alias -Name W -Value Get-Service -Description 'Change Association' -Verbose VERBOSE: Performing the operation "Set Alias" on target "Name: W Value: Get-Service"
Note
Best practice to use an alias is when you are controlling the environment entirely.
Ensure that you have made a note about alias in your script. This helps others to understand and troubleshoot in case of any failures.
If you create more aliases in your machine, you can move them to other machines using the following two cmdlets:
- The
Export-Alias
cmdlet - The
Import-Alias
cmdlet
In this exercise, we will create an alias for the Test-Connection
cmdlet and use it in other machines. The Test
part will be the alias of Test-Connection
. Following is the command to set the alias' name:
New-Alias -Name Test -Value Test-Connection -Description "Testing" -Verbose
Let's test the functionality using the following command:
test localhost
This command returns the following output:
Let's use the Export-Alias
cmdlet and export only this alias using the following command:
Export-Alias -Name test C:\Temp\CustomAlias.txt –Verbose
We will get the following output:
# Alias File # Exported by : ChenV # Date/Time : Tuesday, August 25, 2015 1:57:28 PM # Computer : CHENV "Test","Test-Connection","Testing","None"
Move this text file to another computer and use the Import-Alias
cmdlet to do this.
Look at the following image:
The steps marked in the image we just discussed are explained as follows:
- 1: Here, I used the
Import-Alias
cmdlet and have given the path where I copied the text file - 2: Here, I used the
Get-Alias
cmdlet to verify the existence of the alias - 3: This indicates the result—test -> Test-Connection
- 4: This verifies the functionality of the alias
Note
There is no direct cmdlet to remove an alias, such as Remove-Alias. So, how do we do this?
Alias is one of the items in the PowerShell drive. Yes! We can use the Remove-Item
cmdlet to remove the alias. Execute the following command:
Get-Item Alias:\test
The command that we just discussed returns the following output:
So, you can simply use Remove-Item
to delete the alias. Execute the command as shown in the following image:
Understanding expressions
Windows PowerShell supports regular expressions. We know that PowerShell is built using the .NET framework. So, it strongly supports the regular expressions in .NET.
Developers prefer to use regular expressions to solve complex tasks such as formatting display names, manipulating files, and so on.
Regex can either be used by comparing operators or implementing the .NET class.
Note
The MSDN reference link for the regex class is https://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex%28v=vs.110%29.aspx.
In this topic, we will cover the following:
- Operators and comparison operators
- Implementing regex using the .NET class
- Where do we use regular expressions?
Before we proceed with regular expressions, let's explore a few things about operators.
To know about operators, we can run the following code:
help about_Operators -ShowWindow
The output is illustrated in the following image:
To retrieve help for all the operators in Windows PowerShell, you can run each of the following lines of code and explore their usage:
help about_Arithmetic_Operators help about_Assignment_Operators help about_Comparison_Operators help about_Logical_Operators help about_Type_Operators help about_Split help about_Join help about_Redirection
Windows PowerShell has operators such as arithmetic operators, assignment operators, comparison operators, logical operators, redirection operators, split and join operators, type operators, format operators, static member operators, and so on.
As we are exploring regular expressions, we will focus only on the comparison operators.
Comparison operators are used to compare values and to find the matching pattern. By default, comparison operators are not case sensitive.
We can also perform case-sensitive pattern matching using C
before the operators.
Let's consider both these in the following example:
#Case In-Sensitive "PowerShell" -match "PowerShell"
This command returns the output as true.
#Case Sensitive "PowerShell" -cmatch "powershell"
This command returns the output as false.
Now, it's time for us to use the .NET regular expressions in Windows PowerShell with the comparison operators
Note
The MSDN link for a quick reference guide to regular expressions is https://msdn.microsoft.com/en-us/library/az24scfc%28v=vs.110%29.aspx
Here is a command that uses a regular expression to check whether the first three characters are digits in a given string.
For example, the given string is 123-456-ABC
. Then, the command would be as follows:
"123-245-ABC" -match '^\d{3}'
This command returns the output as true.
"EFG-245-ABC" -match '^\d{3}'
This command returns the output as false.
Now, let's take a look at how the command that we just discussed works, in the following list:
^
: This is the beginning of the string.\d
: This is to check the digits.{3}
: This matches the previous elements n times. In our case, it's three times.
Use the following regular expression to remove the white space characters from any given string:
"Power Shell" -replace '\s' , ''
This returns PowerShell
.
Tip
In case of replacing a string with null with the help of the replace operator
, we can execute the following command:
"Power Shell" -replace '\s'
There is no need to explicitly mention replacing a string with a non-white space character.
The given input string is Power Shell
. The expression removes the white space between Power
and Shell
in the string and outputs the Powershell
word. The white space is removed as explained in the following steps:
- In the preceding code, we used the comparison operator,
–replace
- The
\s
character is the white space character in regex - This is replaced with a non-white space character
We can swap strings using regular expressions.
Consider the given string as FirstName LastName
:
#Given Name: FirstName LastName #Required Output: LastName, FirstName "FirstName LastName" -replace "([a-z]+)\s([a-z]+)" ,'$2, $1'
The commands that we just discussed return the output as LastName, FirstName
.
Consider the given string as FirstName12345 LastName
:
#Given Name: FirstName12345 LastName #Required Output: LastName, FirstName 'FirstName12345 LastName' -replace "\d+" -replace "([a-z]+)\s([a-z]+)" ,'$2, $1'
The commands that we just discussed return the output as LastName, FirstName
.
The output of the previous two expressions is illustrated in the following image:
Similarly, PowerShell allows us to use the regex
class to perform the same tasks. Execute the following code:
[Regex]::IsMatch('PowerShell' , 'PowerShell')
Note
The MSDN TechNet article for the regex class is at the following link:
https://msdn.microsoft.com/en-us/library/system.text.regularexpressions.regex%28VS.80%29.aspx
The IsMatch
method is a member of the regex
class. This method is overloaded, which indicates whether the regular expression finds a match in the input string. This is a simple example to check whether a string contains another string.
Note
The MSDN TechNet article for the regex class members is at the following link:
Developers can easily understand and implement regex. However, IT professionals or system administrators may have difficulties in understanding the arguments to be passed.
What arguments should we pass for members? It's not always necessary to refer to the MSDN article. Instead, we can use PowerShell to find the overloaded definitions.
In the following example, we will use the Replace
method (the public method of the regex class):
[Regex]::Replace
The output of the code that we just discussed is illustrated in the following image:
To remove the numbers from the given string using regex, we execute the following command:
[Regex]::Replace('12String' , '\d{2}' , '')
The command that we just considered returns the output as String
. We get this output as explained in the following steps:
- Using the Regex class, we invoked the public method,
Replace
. - As per the overloaded definitions, we have an option to pass three arguments—string, pattern, and replacement string.
- The
'\d'
shorthand character represents the digits and{n}
checks n times. In our case, it's two times. - This was replaced with empty values.
Understanding objects
In general, a term object is something that we can touch and feel, and the same is applicable for PowerShell as well. An object in PowerShell is a combination of methods and properties:
Objects = Properties + Methods
A property is something that we can get or set. In short, properties store information about the object.
A method is an action to be invoked on a particular object.
Objects are constructed using their types, properties, and methods.
In this section, we will cover the following topics:
- Getting help about objects, properties, and methods
- Exploring objects, properties, methods, and types
Before we explore objects, let's have a look at the help documentation using the following commands:
help about_Objects help about_Properties help about_Methods
The objects that we see in PowerShell are a part of the .NET framework, and PowerShell will allow us to create custom objects as well.
Note
From Windows PowerShell 5.0 onward, we can create objects using a class. This will be covered in the Chapter 2, Unleashing Development Skills Using Windows PowerShell 5.0.
Now, let's explore objects in detail. In the following example, we will use the Get-Date
command:
$Date = Get-Date
Here, $Date
is a variable in Windows PowerShell and Get-Date
is a cmdlet to get the current date and time.
Once we run the preceding code, $date
will be a DateTime
object. Let's take a look at the type of the $Date
variable:
$Date.GetType()
The output of this command is as follows:
The Get-Member
cmdlet is our friend, and helps us to explore the members and properties. To take a look at the available properties and methods, we can run the following code:
Get-Date | Get-Member -MemberType All -Force
The preceding command retrieves all the properties and methods.
The list will be huge; so, to view the properties and methods, we can change the MemberType
value to either Property
or Method
. Let's execute the following code:
Get-Date | Get-Member -MemberType Property -Force
The output of this code is illustrated as follows:
The definitions of the property is shown as {get;}
. Let's explore the property now, as follows:
(Get-Date).DateTime
The command that we just considered displays the current date and time of the local machine.
Now, let's take a look at how the command that we just discussed works, in the following list:
Get-Date
: This is the Windows PowerShell cmdlet..
: This is the property deference operator.- The
DateTime
property shows the current system's date and time.
Alternatively, we can use the DateTime
object, as follows:
[DateTime]::Now
We will get current date and time as output. For example, Tuesday, June 02, 2015 12:15:51 PM
.
The operator used here is the static member operator, ::
.
To invoke the methods of an object, we will follow the same procedure. But, if the method needs arguments, we need to pass it accordingly, as shown in the following command:
Get-Date | Get-Member -MemberType Method –Force
Note
We used the –Force
parameter to retrieve all the methods, including the hidden ones.
Execute the following code:
(Get-Date).AddDays(1)
Here, we added one day to the current day and the output is as follows:
Wednesday, June 03, 2015 12:55:35 PM
Alternatively, we can write ([DateTime]::Now).AddDays(1)
.
The $psISE
object is the root object of the Integrated Scripting Environment. Using this we can toggle settings of the ISE, as follows:
$psISE | GM -Force
The output of the command we just discussed retrieves all the members of $PSISE
. One of the property options that hold all the options of the ISE is as follows:
$psISE.Options
To modify the zoom, use the following code:
$psISE.Options.Zoom = 150
To modify the Intellisense timeout seconds, we execute the following code:
$psISE.Options.IntellisenseTimeoutInSeconds = 5
To change the script pane's background color, we execute the following code:
$psISE.Options.ScriptPaneBackgroundColor = 'Green'
Understanding pipelines
A Windows PowerShell pipeline is used to join two or more statements with a pipeline operator. The Pipeline operator is '|'.
We have used pipelines in previous examples; let's know about pipeline use case scenario.
In this section, we will cover the following:
- Using a pipeline operator
- Where to use a pipeline operator
Windows PowerShell is designed to use pipeline. Here's an example of pipelines:
Command1 | Command2 | Command3
Here, Command1
sends the object to Command2
; the processed object will then be sent to Command3
, which will output the results. Take a look at the following command:
help about_Pipelines -ShowWindow
A pipeline works in the following way:
- The parameter must accept input from a pipeline (however, not all do so)
- The parameter must accept the type of object being sent or a type that the object that can be converted to
- The parameter must not already be used in the command
Now, let's take a look at the following example:
PS C:\> Get-Service -Name BITS Status Name DisplayName ------ ---- ----------- Running BITS Background Intelligent Transfer Ser...
The Get-Service
cmdlet gets the object representing the BITS service.
Using the
Stop-Service
cmdlet, we can stop the service. The -Verbose
parameter is to show the operation handled, as shown in the following code:
Get-Service -Name BITS | Stop-Service -Verbose
The output of the command we just discussed is illustrated in the following image:
Using pipeline, we can do many more tasks, such as sorting, grouping, looping, and so on.
How do we find the parameter that accepts pipeline? Using help and pipeline, we can do this as shown in the following code:
help Get-Service -Parameter * | Select name , PipelineInput
The output of the command we just discussed is illustrated in the following image:
To retrieve the Windows services of the remote machine, SharePoint001
, we can write the command as follows:
'SharePoint001' | %{Get-Service -ComputerName $_}
In short, the pipeline passes the output to another command so that the next command has something to work with or simply connects the output to other commands. This helps IT professionals automate tasks such as inventorying, reporting, and so on.
Exporting a running process to a CSV file
Let's take a look at the following command:
Get-Process | Export-csv C:\Temp\Process.csv ` -NoTypeInformation -Encoding UTF8
Refer to the following image:
The points marked in the image are explained as follows:
- 1: The
Get-Process
cmdlet is used to retrieve a running process in your local machine. Alternatively, thegps
alias can be used - 2: This is the pipeline operator used to pass the output to the next command
- 3: The
Export-csv
cmdlet is used to save the output in theCSV
format - 4: This is the complete path of the output file
- 5: This is the PowerShell line continuation character
- 6: The
NotypeInformation
switch parameter is used to avoid#TypeInformation
in theCSV
header - 7: The
Export-CSV
cmdlet has an encoding parameter, which can beASCII
,UTF7
,UTF8
,BigEndianUnicode
,OEM
, or so on
The CSV output is shown in following image:
I prefer to export data in the XML
format using the Export-Clixml
cmdlet because it holds a lot more information. Take a look at the following command:
Get-Process | Export-Clixml C:\Temp\Process.xml
The output is shown in the following image:
Using Import-Clixml
and Import-Csv
, we can view the output we exported:
Import-Clixml C:\Temp\Process.xml
The output of the command we just discussed is shown in the following image:
Using pipelines, we can connect multiple commands and get effective solutions, as explained in the following list:
- We can start, stop, or set a service
- We can export the output to report, for inventories, and so on.
- They connect commands and display the output as required
- They help in sorting, filtering, and formatting objects
Understanding filtering and formatting
In Windows PowerShell, filtering and formatting are used in most places to get the output in the desired format. Select-Object
is very useful cmdlet to filter.
In this section, we will cover the following topics:
- Basics of filtering
- Basics of formatting
Consider the following command:
Get-Command -Noun Object
The output is as shown in the following image:
Use the following Help
commands:
Help Select-Object -Examples Help Where-Object -Examples
Using the Select-Object
cmdlet, we can select the first and last n items from the collection of objects. The Select-Object
cmdlet can be used to retrieve only unique values (ignoring duplicates).
Now, let's explore Select-Object
for filtering. Let's consider that we have a set of objects from 65
to 90
; to select the first 10
, we need to pipe and use Select-Object
, as shown in the following command:
65..90 | Select -First 10
We will get the output as 65
to 74
.
Consider the following command:
1,2,2,3 | Select -Unique
Here, we will get the output as 1
, 2
, and 3
.
Take a look at the following command:
1,2,2,3 | Select -Last 1
Here, we will get the output as 3
.
Consider the following command:
1,2,2,3 | Select -SkipLast 1
We will get the output as 1
, 2
, and 2
.
Take a look at the following command:
1,2,2,3 | Select -Skip 2
Here, we will get the output as 2
and 3
.
To get help for all the parameters in Select-Object
, use the following code:
help Select-Object -Parameter *
The Where-Object
cmdlet is used to filter data returned by the other cmdlet. This cmdlet accepts comparison operators.
Let's explore the syntax of the Where-Object
cmdlet, as follows:
(help Where-Object).Syntax
The output of the command we just considered is illustrated in the following image:
Consider an array from 1
to 5
. To select values greater than 3
, we use the Where-Object
alias, ?
, next to the pipeline operator, as shown in the following command:
1..5 | ? {$_ -gt 3}
From Windows PowerShell 4.0 onward, we can avoid pipelines for the ForEach
and Where
objects, as follows:
(65..90).ForEach({[char][int]$_})
The output of the code we just discussed is shown in the following image:
Now, let's take a look at how this works in the following list:
- The
(65..90)
range uses the range operator,..
; these are the ASCII values of A-Z - We used the dereference operator,
.
, to invoke theForeach
method - The
Foreach
method accepts expressions and arguments, as shown in the code we just considered.
Now, let's take a look at the following code:
(1..10).Where({$_ -ge 5})
The output of this code is shown in the following image:
Now, let's take a look at how this works in the following list:
- The
(1..10)
range uses the range operator,..
; this is the1
to10
array of the object - We used the dereference operator,
.
, to invoke theWhere
method - The
Where
method accepts the expression, mode, and number to return
We can use the Where
statement with different modes. In the following example, we will select the first three values, where the number is greater than or equal to 5
. Now, let's consider the following code:
(1..10).Where({$_ -ge 5}, 'First' , 3)
The output of this code is illustrated in the following image:
Now, let's take a look at how this works in the following list:
- The
(1..10)
uses the range operator,..
, and this is the1
to10
array of the objects - We used the dereference operator to invoke the
Where
method - In this expression,
{$_ -ge 5}
is an object greater than5
,First
is the mode, and3
is the value for the number to be returned.
Similarly, we can use the Split
mode as well. Using this, we can split the given collection of objects, as shown in the following code:
$section1 , $section2 = (1..100).Where({$_ -le 50} , 'Split' , 0) $section1 $section2
The $section1
variable contains values from 1
to 50
, and the $section2
variable contains values from 51
to 100
.
Note
Avoid pipelines as much as you can. Use appropriately, because in larger script we may end up having a performance issue.
Use Measure-Command
and analyze the performance of commands using pipelines.
Here is a table comparing commands using pipeline with those that don't use a pipeline:
With pipeline |
Result in milliseconds |
Without pipeline |
Result in milliseconds |
---|---|---|---|
|
4 |
|
1 |
|
42 |
|
2 |
PowerShell formatting
Windows PowerShell has a set of cmdlets that allows us to format the output. To find the cmdlets to format, use Verb Format
to search, as shown in the following code:
Get-Command -Verb Format
Let's take a look at the default formatting of Windows PowerShell by executing the following the cmdlet:
Get-Process | Select –First 5
The output of this code is as follows:
The headers are not exactly property names. This formatting is done using the file name, DOTNETTYPES.FORMAT.PS1XML
.
The location of the file is $PSHome
. Take a look at the following image:
The following list explains the points marked in the preceding image:
- 1: The type name of
Get-Process
isSystem.Diagnostics.Process
- 2: For
NPM(K)
, refer to the image preceding the previous image - 3: For
PM(K)
, refer to the image preceding the previous image
The first thing we see in the XML
file is the following warning:
Do not edit or change the contents of this file directly. Please see the Windows PowerShell documentation or type.
Use the following command to obtain more information:
Get-Help Update-TypeData
So, let's not make any kind of modifications. Instead, let's take a look at the cmdlets to make minor modifications as desired, as shown in the following image:
In the following example, we will use the Where
method to select n items required to keep the output precise and short.
The help
command to format the table is as follows:
help Format-Table -ShowWindow
Let's select the first five running services and format them as follows:
(Get-Service).Where({$_.Status -eq 'Running'},'First',5) | Format-Table
This code outputs the default formatting.
Using the Format-Table
cmdlet, we can hide the headers, auto size the table, use the expression, and much more. Let's consider the following command:
(Get-Service).Where({$_.Status -eq 'Running'},'First',5) | Format-Table -HideTableHeaders
The output of the command we just discussed is shown in the following image:
The default formatting of Windows PowerShell is not great, but it allows us to customize the format as required. The report we deliver to IT Management should be precise and readable. Using Windows PowerShell, we can achieve this.
Reports can be in the HTML, XML, CSV, or other desired formats.
Using an expression is allowed in Format-Table
inside the {}
script block token. Run the following command:
Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" | Format-Table Name,@{n="Freespace(byte)";e={"{0:N0}" -f $_.FreeSpace};a="center"}
The following image shows all three alignments: left, right, and center:
Here is another example of a command to format the date in the day/month/year format using a format operator:
"Custom Date Format: {0},{1},{2}" -F (Get-Date).Day , (Get-Date).Month , (Get-Date).Year
To view the LastWriteTime.DayOfWeek
file, run the following command:
Get-ChildItem C:\Temp | Ft name , @{n="Day of Week" ; E = {$_.LastWriteTime.DayOfWeek}}
The following is the command to custom format using the Format-Custom
cmdlet:
Get-Service | Format-Custom
The default output of the command we just discussed is as follows:
Use the Get-FormatData
cmdlet to view the formatting data from the Format.ps1xml
formatting files. In this demo, we will try to customize the default format in the current session, as follows:
help Get-FormatData -ShowWindow help Export-FormatData -ShowWindow help Update-FormatData -ShowWindow
Perform the following steps:
Note
In this demo, we will change the column header to test. This will break the output ONLY in the current session.
- Identify the type name of the command. For example, here, we will use the
Get-Process | GM
command. - The
TypeName
parameter isSystem.Diagnostics.Process
, as shown in the following command:Get-FormatData -TypeName System.Diagnostics.Process | Export-FormatData -Path C:\Temp\TestView.Format.PS1XML
- Now, append the text of the column header in the
PS1XML
file in theTemp
folder, as shown in the following command:Update-FormatData -PrependPath C:\Temp\TestView.Format.PS1XMl
- The
Get-Process
command returns the output as shown in the following image:
Exploring snippets in the PowerShell ISE
There is a cool way in the PowerShell ISE to reduce typing; however, before discussing PowerShell scripting, let's take a look at snippets.
Snippets are nothing but commonly used code. In PowerShell, we very often use functions, advanced functions, comment blocks, and so on.
Using an ISE, we don't type out the structure of the function. Instead, we can right-click on Script Pane and select Start Snippets or press Ctrl + J. This shows a menu of the available snippets.
The following image illustrates the snippets in the PowerShell ISE:
We need not be limited to only the available snippets; we can create our own snippets. In this demo, we will try to create a snippet. This is a simple snippet to add a mandatory parameter, which we can reuse in any of our functions.
This helps developers and IT professionals do the scripting faster.
There is no need for more typing; we just need to add a snippet wherever we need reusable codes, as shown in the following code:
$m = @' Param( [Parameter(Mandatory=$true)] [String] $String ) '@ New-IseSnippet -Text $m -Title Mandatory -Description 'Adds a Mandatory function parameter' -Author "Chen V" -Force
Let's take a look at how this works:
- The
$m
variable holds the skeleton code - The
New-IseSnippet
command is the one available in the module's ISE - We used a few parameters with
Text
, which is the skeleton code;Title
, which can be any friendly name;Description
, which describes the snippet in short; andAuthor
, which is the author's name - After the code is executed, it creates a
PS1XML
file in the location,$home\Documents\WindowsPowerShell\Snippets\
- The file name will be your title—in this case it's
Mandatory.Snippets.PS1XML
Note
We can copy and place it any machine—pressing Ctrl + J will show Mandatory
in the snippets.
The following image illustrates the output of the newly created snippet:
The following are the steps that explain the points marked in the preceding image:
- 1: The name
Mandatory
is the title of our new snippet - 2: The Description field is shown in the pop-up box
- 3: The Path field is the path of the
PS1XML
file - 4: This is the snippet code or skeleton code
Getting started with PowerShell scripting
Let's take a look at scripting in the cmdlet style.
We've arrived at a place from where we can explore PowerShell scripting with the knowledge of the previous topics. Wait! We haven't covered all that we need for scripting in PowerShell. Before we begin discussing scripting, we should know more about the scripting principles, using variables, commenting, writing help, and so on.
Here are a few principles of scripting:
If you want to deliver scripts to your organization or community, it's good to create the variables and follow the standard naming conventions. Do not use plain text passwords in the scripts, and avoid technical jargon in the comment blocks. Ensure that the script is readable for others.
Using Windows PowerShell scripting, we can perform complex tasks with the help of imperative commands. The scripting language supports branching, variables, and functions.
From now on, we will use the PowerShell ISE for its ease of use and benefits.
In this section, we will cover the following topics:
- Using variables
- The basics of Windows PowerShell scripting
- Writing the functions and advanced functions
Let's now discuss using variables.
A variable is used to store information, and it is the result of a running script.
Here's an example:
$value = Read-Host "Enter Value 1"
This script prompts for user input; once the value is entered, it stores it in the $value
variable, as shown in the following image:
Note
Following are a few points to note about variables:
- They can be string or integer, and they allow special characters
- They should be used precisely—don't use special characters in their name
- You need to use the standard naming conventions that are easily understandable
- You can use the automatic, preference, and environment variables
- You should modify the preference variables only if required
Using the New-Variable
cmdlet, a variable can be created along with a scope definition. In the following example, let's create a variable name, ws
, which holds the value of the Windows service, and sets the scope to Global
:
New-Variable -Name 'ws' -Value (Get-Service) -Scope Global
Using Remove-Variable
, it can be removed, as shown in the following code:
Remove-Variable -Name ws -Verbose
Windows PowerShell scripting is used to automate your daily tasks. It may be anything such as reporting, server health checkup, performing tasks such as restarting services, stopping services, deploying solutions, installing Windows features, and much more.
The following points need to be considered while creating PowerShell scripting:
- Keep the PowerShell code simple and neat.
- Follow the same indentation throughout the code.
- Make a clear, comments-based help.
- Comment on your parameters with descriptions. This allows others to get help about the parameters.
Let's write a simple script that prints hello world on the screen:
#Windows PowerShell Script to Retrieve Windows Services Write-Host "Hello, World!" -ForegroundColor Green
To run the PowerShell script, you need to call the script using a dot (.
) operator followed by backward (\
) slash.
The extension of the PowerShell script file should be .PS1
.
The output of the preceding code is illustrated in the following image:
Let's create a script that does a basic system inventory.
This PowerShell script will create a CSS
file for styles, query the basic system information, convert to an HTML
file, and open up after the script is completely executed, as shown in the following code:
$UserName = (Get-Item env:\username).Value $ComputerName = (Get-Item env:\Computername).Value $filepath = (Get-ChildItem env:\userprofile).value Add-Content "$Filepath\style.CSS" -Value " body { font-family:Calibri; font-size:10pt; } th { background-color:black; color:white; } td { background-color:#19fff0; color:black; }" Write-Host "CSS File Created Successfully... Executing Inventory Report!!! Please Wait !!!" -ForegroundColor Yellow #ReportDate $ReportDate = Get-Date | Select -Property DateTime |ConvertTo-Html -Fragment #General Information $ComputerSystem = Get-WmiObject -Class Win32_ComputerSystem | Select -Property Model , Manufacturer , Description , PrimaryOwnerName , SystemType |ConvertTo-Html -Fragment #Boot Configuration $BootConfiguration = Get-WmiObject -Class Win32_BootConfiguration | Select -Property Name , ConfigurationPath | ConvertTo-Html -Fragment #BIOS Information $BIOS = Get-WmiObject -Class Win32_BIOS | Select -Property PSComputerName , Manufacturer , Version | ConvertTo-Html -Fragment #Operating System Information $OS = Get-WmiObject -Class Win32_OperatingSystem | Select -Property Caption , CSDVersion , OSArchitecture , OSLanguage | ConvertTo-Html -Fragment #Time Zone Information $TimeZone = Get-WmiObject -Class Win32_TimeZone | Select Caption , StandardName | ConvertTo-Html -Fragment #Logical Disk Information $Disk = Get-WmiObject -Class Win32_LogicalDisk -Filter DriveType=3 | Select SystemName , DeviceID , @{Name="size(GB)";Expression={"{0:N1}" -f($_.size/1gb)}}, @{Name="freespace(GB)";Expression={"{0:N1}" -f($_.freespace/1gb)}} | ConvertTo-Html -Fragment #CPU Information $SystemProcessor = Get-WmiObject -Class Win32_Processor | Select SystemName , Name , MaxClockSpeed , Manufacturer , status |ConvertTo-Html -Fragment #Memory Information $PhysicalMemory = Get-WmiObject -Class Win32_PhysicalMemory | Select -Property Tag , SerialNumber , PartNumber , Manufacturer , DeviceLocator , @{Name="Capacity(GB)";Expression={"{0:N1}" -f ($_.Capacity/1GB)}} | ConvertTo-Html -Fragment #Software Inventory $Software = Get-WmiObject -Class Win32_Product | Select Name , Vendor , Version , Caption | ConvertTo-Html -Fragment ConvertTo-Html -Body "<font color = blue><H4><B>Report Executed On</B></H4></font>$ReportDate <font color = blue><H4><B>General Information</B></H4></font>$ComputerSystem <font color = blue><H4><B>Boot Configuration</B></H4></font>$BootConfiguration <font color = blue><H4><B>BIOS Information</B></H4></font>$BIOS <font color = blue><H4><B>Operating System Information</B></H4></font>$OS <font color = blue><H4><B>Time Zone Information</B></H4></font>$TimeZone <font color = blue><H4><B>Disk Information</B></H4></font>$Disk <font color = blue><H4><B>Processor Information</B></H4></font>$SystemProcessor <font color = blue><H4><B>Memory Information</B></H4></font>$PhysicalMemory <font color = blue><H4><B>Software Inventory</B></H4></font>$Software" -CssUri "$filepath\style.CSS" -Title "Server Inventory" | Out-File "$FilePath\$ComputerName.html" Write-Host "Script Execution Completed" -ForegroundColor Yellow Invoke-Item -Path "$FilePath\$ComputerName.html"
The output of this code is illustrated in the following image:
Let's take a look at how to write PowerShell functions with comments:
<# .Synopsis To add two integer values .DESCRIPTION Windows PowerShell Script Demo to add two values This accepts pipeline values .EXAMPLE Add-Values -Param1 20 -Param2 30 .EXAMPLE 12,23 | Add-Values #> function Add-Values { [CmdletBinding()] [Alias()] [OutputType([int])] Param ( # Param1 help description [Parameter(Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true, Position=0)] #Accepts Only Integer [int]$Param1, #Accepts only integer [Parameter(Mandatory=$true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName=$true, Position=0)] [int]$Param2 ) Begin { "Script Begins" } Process { $result = $Param1 + $Param2 } End { $result } }
This is not a good script, but I will use this to demonstrate a PowerShell function with comments in order to help explore the use of help.
The following is an explanation of how this code works:
- The most important part is the comment block. Let's take a look at the following code snippet:
<# .Synopsis To add two integer values .DESCRIPTION Windows PowerShell Script Demo to add two values This accepts pipeline values .EXAMPLE Add-Values -Param1 20 -Param2 30 .EXAMPLE 12,23 | Add-Values #>
- We need to provide a short description and synopsis of the script, as we did in this example. This will help others explore and know the usage of the script.
- Then, we used the
Function
keyword and followed the standard Verb-Noun naming convention. In this example, we usedAdd-Values
. - We parameterized our script using the
Param
block. - We named the parameter as applicable and added a comment/help description on top of the parameter.
- We used the
Begin
,Process
, andEnd
blocks. I inserted the addition code in theProcess
block.
Note
To simplify all of these steps, open the ISE, press Ctrl + J, and select CMDLET
(advanced function).
To execute this script, we need to use the .\Filename.PS1
command. Take a look at the following image:
The following is an explanation of the steps marked in the image we just considered:
- 1: We executed the
Code3
script - 2: We used the
Get-Help
cmdlet to read about the custom function - 3: This is the name of the command
- 4: This is the customized synopsis
- 5: The
SYNTAX
field is autogenerated - 6: This is the customized description
- 7: The
RELATED LINKS
field is empty because we haven't included it in our comment block - 8: The
REMARKS
field is default
To view only the examples, we will use the following command:
Get-Help Add-Values -Examples
The output of this code is illustrated in the following image:
The following is an explanation of the points marked in the preceding image:
- 1: The
Get-Help
cmdlet is used to get help of the custom function or script - 2: This is the name of the cmdlet/function
- 3: This shows
EXAMPLE 1
- 4: This shows
EXAMPLE 2
Similarly, we can view only parameters. The help description given above each parameter appears using the following code:
Get-Help Add-Values -Parameter *
Take a look at the following image:
The following are a few commands that use the help
command:
help about_Functions help about_Functions_Advanced_Methods help about_Functions_Advanced_Parameters help about_Functions_CmdletBindingAttribute help about_Functions_OutputTypeAttribute
Let's take a look at how to write advanced functions.
Advanced functions are similar to compiled cmdlets but not exactly the same.
Here is a table comparing advanced functions and compiled cmdlets:
Advanced functions |
Compiled cmdlets |
---|---|
These are designed using the PowerShell script. |
These are designed using a .NET framework, such as C#, and compiled as DLL. |
The actual work will be done in the process blocks. |
The actual work will be done in the process records. |
An advanced function can be created easily using the PowerShell ISE. |
A compiled cmdlet can be created easily using the Visual Studio class library. |
The performance of advanced functions is slower compared to a compiled cmdlet. |
Compiled cmdlets are faster. |
In this section, we will discuss both writing an advanced function code using the PowerShell ISE and a compiled cmdlet using the Visual Studio C# class library.
Let's take a look at how to create a compiled cmdlet using the Visual Studio C# class library. Execute the following code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Management; using System.Management.Automation; using System.IO; namespace Windows_Management { [Cmdlet(VerbsCommon.Clear, "TemporaryInternetFiles")] public class WindowsManagement : PSCmdlet { protected override void ProcessRecord() { //Delete Internet Cache Files and Folders string path = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("Clearing Temporary Internet Cache Files and Directories....." + path); System.IO.DirectoryInfo folder = new DirectoryInfo(path); foreach (FileInfo files in folder.GetFiles()) { try { files.Delete(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } foreach (DirectoryInfo Directory in folder.GetDirectories()) { try { Directory.Delete(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } Console.WriteLine("Done Processing!!!"); Console.ResetColor(); } } } namespace clearInternetexplorerHistory { [Cmdlet(VerbsCommon.Clear, "IEHistory")] public class clearInternetexplorerHistory : PSCmdlet { protected override void ProcessRecord() { // base.ProcessRecord(); string path = Environment.GetFolderPath(Environment.SpecialFolder.History); Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine("Clearing Internet Explorer History....." + path); System.IO.DirectoryInfo folder = new DirectoryInfo(path); foreach (FileInfo files in folder.GetFiles()) { try { files.Delete(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } foreach (DirectoryInfo Directory in folder.GetDirectories()) { try { Directory.Delete(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } Console.WriteLine("Done Processing!!!"); Console.ResetColor(); } } } namespace UserTemporaryFiles { [Cmdlet(VerbsCommon.Clear, "UserTemporaryFiles")] public class UserTemporaryFiles : PSCmdlet { protected override void ProcessRecord() { //base.ProcessRecord(); string temppath = System.IO.Path.GetTempPath(); System.IO.DirectoryInfo usertemp = new DirectoryInfo(temppath); Console.WriteLine("Clearing Your Profile Temporary Files..." + temppath); foreach (FileInfo tempfiles in usertemp.GetFiles()) { try { tempfiles.Delete(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } Console.WriteLine("Done Processing!!!"); foreach (DirectoryInfo tempdirectory in usertemp.GetDirectories()) { try { tempdirectory.Delete(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex); } } } } }
Once the DLL is compiled, we can import it as a module in Windows PowerShell.
The output of the command we just discussed is illustrated in the following image:
Now, let's consider the advanced functions in Windows PowerShell.
Advanced functions are more robust, can handle errors, support verbose and dynamic parameters, and so on.
Let's take a look at the small advanced functions used to retrieve system information, as follows:
function Get-SystemInformation { [CmdletBinding()] [Alias()] [OutputType([int])] Param ( # Param1 help description [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, ValueFromPipeline = $true, HelpMessage = "Enter Valid Host Names", Position=0)] [Alias('Host')] [ValidateCount(0,15)] [String[]]$ComputerName, # Param2 help description [int] $Param2 ) Begin { } Process { foreach($cn in $ComputerName) { $cs = Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName $cn $baseboard = Get-CimInstance -ClassName Win32_BaseBoard -ComputerName $cn $properties = New-Object psobject -Property @{ ComputerName = $cs.Caption Model = $cs.Model ComputerOwner = $cs.PrimaryOwnerName Bootupsate = $cs.BootupState BaseBoardSerialNumber = $baseboard.SerialNumber BaseBoardManufacturer = $baseboard.Manufacturer } $properties } } End { } } 'localhost' , 'localhost' | %{Get-SystemInformation -ComputerName $_}
The output of the code we just discussed is illustrated in the following image:
As we have already covered the topic of adding help for PowerShell functions, I ignored it in the advanced functions section. Remember, we do have snippets in the ISE to create advanced functions. It's simple; just right-click on the script pane, select Start snippet, and then choose the Advanced function complete option.
Start documenting the synopsis, description, and help for parameters and build your code in the process block. This is very easy and handy to build advanced scripts using PowerShell.
Before building scripts, use the Measure-Command
cmdlet and think about optimization. This will help in performance.