PowerShell Tricks

Feng Gao
7 min readNov 30, 2021

--

PowerShell is a script language, compared to Bash on Linux, which used to run on the Microsoft Windows platform. But now, it has evolved to PowerShell Core which isn't scoped to Windows only.

PowerShell tightly integrates with .NET but embodies a lot of script language features. I have observed a lot of guys are using PowerShell with OOP style. This blog will explore some ways for PowerShell advanced usage.

1. Array Parameter

Suppose a function has a parameter, accepting array type.

Get-Process -Name <string[]>

At first glance, we need to prepare a array before invoking this function.

Get-Process -Name @("powershell", "explorer")

But, PowerShell can convert the comma separated string to array implicitly.

Get-Process -Name powershell, explorer

2. Array

If a function return more than one variables, the result would be array.

function Get-Something {
return 'Hello', 'World'
}
(Get-Something).GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

Like Python , we can also select the part of elements from an array.

$range = 1, 2, 3, 4, 5
$range[2..4]
3
4
5

Some string method can be applied against the the string array. In this way, each element of this array will call the method respectively.

("azz", "abz", "zzz").trim("z")
a
ab

3. Splatting

If a function have a couple of parameters that cannot display in single line. you have two options:

  • Wrap the editor
  • Use line breaker

PowerShell has a functionality called Splatting that can group the parameters

function Get-Process {
param (
[string[]]Name,
[int[]]Id
)
}
$process = @{
Name = 'explorer'
}
Get-Process @process

We choose hashtable to group all the argument and then pass it to the function.

@ instead of $

4. Property

For a custom object, it can add custom property dynamically. How retrieve the property value if don’t know the property name exactly. PowerShell’s . operation can do that in the runtime.

$object = [pscutomobject]@{"some name" = "value"}
$object."some name" # value
$object.{some name} # value
$prop = "some name"
$object.$prop # value

5. Select-Object

Select-Object is widely used command in PowerShell daily work. It can achieve couples of goals via parameter combination.

  • Select properties: Get-Process | Select-Object -Property Name, Id
  • Select properties with wildcards: Get-Process | Select-Object -Property Name, *Memory
  • Select properties but exclude ones: Get-Process | Select-Object -Property * -Exclude Memory
  • Get first objects: Get-Process | Select-Object -First 2
  • Get last objects: Get-Process | select-Object -Last 2
  • Get object but skip ones: Get-Process | Select-Object -Skip 4 -First 1
  • Select property but expand them: Get-Process | Select-Object -ExpandProperty Name

6. Array comparison operator

In general, the comparison operators are used with scalar values. The result should be true or false . When an array applies to the comparison operator , the result consists of elements that match the criteria.

1, $null -ne $null # 1
1,2,3,4 -ge 3 #3, 4
'One', 'Two', 'Three' -like '*e*' # 'one', 'three'

What if it is used in test whether any element match criteria.

$array = 1, 2, 3
if ($array -gt 3) {
Write-Host "Greater than 3"
}
# greater than 3

7. Redirection

In Bash, it has three major stream. stdout, stderr and stdin . It also has those concepts in the PowerShell as well.

It means you can direct the output for a specific stream.

function Test-Redirect {
'This is standard out'
Write-Error 'This is an error'
Write-Warning 'This is a warning'
}
$stdOut = Test-Redirect 3 > "warning.txt" 2> 'error.txt'
Get-Content 'Warning.txt'
# This is a warning
Get-Content 'error.txt'
# This is an error

It also supports to discard the output, like ls > /dev/null used in bash.

Get-Process > $null
# or
Get-Process | out-null

8. Confirm/WhatIf/Force

Confirm , Force and Whatif are used to cooperate with other commands which are going to make change.

  • Confirm parameter will introduce a prompt before actually complete a job.
> cd $env:TEMP
> New-Item data.txt
> Remove-Item data.txt -Confirm
Confirm
Are you sure you want to perform this action?
Performing the operation "Remove File" on target "C:\\Users\\fenga\\AppData\\Local\\Temp\\data.txt".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):

Sometimes, the prompt shows up by default. Setting Confirm:$false can bypass the prompt.

Clear-RecycleBin -Confirm:$false

A question to raise is that how to determine the prompt shows up? In PowerShell session, it has ConfirmPreference variable.

> $ConfirmPreference
High

Each command has the CmdletBindling attribute with ConfirmImpact property, e.g.

[CmdletBinding(ConfirmImpact='High')]

When executing each command, it will compare the $ConfirmPreference and ConfirmImpact .

> $ConfirmPreference="Low"
> New-Item data.txt
Confirm
Are you sure you want to perform this action?
Performing the operation "Create File" on target "Destination: C:\\Users\\fenga\\AppData\\Local\\Temp\\data.txt".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"):
  • WhatIf presents with a simple statement that should state what would have been done.
> New-Item data.txt -Force
> Remove-Item data.txt -WhatIf
What if: Performing the operation "Remove File" on target "C:\\Users\\fenga\\AppData\\Local\\Temp\\data.txt".

By adding WhatIf parameter, it didn't delete data.txt actually. Same with Confirm parameter, the WhatIfPreference variables holds on a Boolean value.

$WhatIfPreference=$true
New-Item newfile.txt
What if: Performing the operation "Create File" on target "Destination: C:\\Users\\fenga\\AppData\\Local\\Temp\\newfile.txt".

How to define customized WhatIf ? It can be achieved by $pscmdlet.ShouldProcess which wrap the code to execute.

function Test-ShouldProcess {
[CmdletBinding(SupportsShouldProcess)]
param()

if($pscmdlet.ShouldProcess("SomeObject", "delete")) {
Write-Host "Deleting SomeObject" -Foreground Cyan
}
}
Test-ShouldProcess
What if: Performing the operation "delete" on target "SomeObject".
  • Force parameter is widely used in command that avoid the prompt.
New-Item a.txt
Remove-Item a.txt -Force

9. Variable Scopes

PowerShell can limit the access to variables, aliases, functions by scope. It supports the following scopes:

  • Global: The scope takes effect when PowerShell starts or when you create a new session or runspace.
  • Local: The current scope. it can be the global scope or any other scope.
  • Script: It is created while a script file runs.

Note: Private is not a scope. It is an option to change the visibility of variable.

It has three basic rules of scope

  • Scope may nest. An outer scope is referred to as the parents scope
  • A variable in the scope is visible in which it was created and in any child scopes. Unless you explicitly make it private.
  • A variable can be only changed in the scope in which it was created.

Sample 1

$local:thisValue = "Some Value"
"From Local: $local:thisValue" # From Local: Some Value
"From Global: $global:thisValue" # From Global: Some Value
function Test-ThisScope {
"From Local: $local:thisValue"
"From Global: $global:thisValue"
}
Test-ThisScope
# From Local:
# From Global: Some Value

$local:thisValue is defined in local scope but it's also means global scope. So, it is accessible in line 2 and line 3. In Test-ThisScope which is the child scope of global scope, it isn't accessible with local scope any more but works in global scope.

Sample 2

$thisValue = "Some Value"function Test-ThisScope {
"From Local: $local:thisValue"
"From Global: $global:thisValue"
"Without scope: $thisValue"
}
Test-ThisScope
# From Local:
# From Global: Some Value
# Without scope: Some Value

$thisValue doesn't place scope prefix and it means local scope. Test-ThisScope is child scope. If doesn't specify the scope, it will resolve by looking up the parent.

Sample 3

$private:thisValue="Some Value"
"From global: $global:thisValue" #From global: Some Value
function Test-ThisScope {
"Without scope: $thisValue"
"From Private: $private:thisValue"
"From global: $global:thisValue"
}
Test-ThisScope
# Without scope:
# From Private:
# From global:

None of value is accessible in the Test-ThisScope function since it thisVAlue is defined as global scope but with private prefix.

10. Dot Source

Script and functions follows all the rules of rule. But, you can add a script or function to the current scope by using dot source. By doing so, any function, variables that the script creates are available in the current scope.

Here we define a Sample.ps1 PowerShell file.

# Sample.ps1
$ThisValue = "Hello World"
function Test-Hello {
Write-Host "This is from sample"
}

We can import ThisValue and Test- to the current scope.

. ./Sample.ps1
$ThisValue # Hello World
Test-Hello # This is from sample

11. PSProvider / PSDriver

In PowerShell, the data has multiple source. They can be filesystem, registry, certificate, and so on.

PS> Get-PSProvider
Name Capabilities Drives
---- ------------ ------
Registry ShouldProcess {HKLM, HKCU}
Alias ShouldProcess {Alias}
Environment ShouldProcess {Env}
FileSystem Filter, ShouldProcess, Credentials {C,D, Temp}
Function ShouldProcess {Function}
Variable ShouldProcess {Variable}

Each provider has their own Drives . The driver is the place where store the data.

> Get-PSDrive
Name Used (GB) Free (GB) Provider Root
---- --------- --------- -------- ----
Alias Alias
C 135.84 340.23 FileSystem C:\\
Cert Certificate \\
D 0.66 236.72 FileSystem D:\\
Env Environment
Function Function
HKCU Registry HKEY_CURRENT_USER
HKLM Registry HKEY_LOCAL_MACHINE
Temp 135.84 340.23 FileSystem C:\\Users\\gaufung\\AppData\\Local\\Tem…
Variable Variable
WSMan WSMan

In other words, we could walk through the different drives via Set-Location command.

Set-Location Cert:\\
Get-ChildItem
# Location : CurrentUser
# StoreNames : {AuthRoot, My, CA, Root…}
# Location : LocalMachine
# StoreNames : {Disallowed, SmartCardRoot, Trust, TestSignRoot…}

12. Comment-Based Help

As we all know, Get-Help command can help us display more details about a command. How can I leverage it when exporting our method to us? Yes, the answer is the MAML . It has several concepts:

  • .SYNOPSIS (Mandatory: Brief introduction for the function)
  • .DESCRPTION (Mandatory: Detailed introduction for the function)
  • .PARAMETER <Name>(Optional: parameter name explaination)
  • .EXAMPLE (Optional: example of this function usage)
  • .INPUTS (Optional)
  • .OUTPUTS (Optional)
  • .NOTES (Optional)
  • .LINK (Optional)
function Get-Something {
<#
.SYNOPSIS
Brifly describe the main action performed by Get-Something
.DESCRIPTION
A detailed descrption of the activities of Get-Something
#>
}
PS > get-help get-somethingNAME
Get-Something
SYNOPSIS
Brifly describe the main action performed by Get-Something
SYNTAX
Get-Something [<CommonParameters>]
DESCRIPTION
A detailed descrption of the activities of Get-Something
RELATED LINKSREMARKS
To see the examples, type: "Get-Help Get-Something -Examples"
For more information, type: "Get-Help Get-Something -Detailed"
For technical information, type: "Get-Help Get-Something -Full"

--

--

Feng Gao
Feng Gao

Written by Feng Gao

A software developer in Microsoft at Suzhou. Most articles spoken language is Chinese. I will try with English when I’m ready

Responses (1)