Welcome PowerShell User! This recipe is just one of the hundreds of useful resources contained in the PowerShell Cookbook.
If you own the book already, login here to get free, online, searchable access to the entire book's content.
If not, the Windows PowerShell Cookbook is available at Amazon, or any of your other favourite book retailers. If you want to see what the PowerShell Cookbook has to offer, enjoy this free 90 page e-book sample: "The Windows PowerShell Interactive Shell".
When working with variables and commands, some concepts feel too minor to deserve an entire new command or function, but the readability of your script suffers without them.
A few examples where this becomes evident are date math (yesterday becomes
(Get-Date).AddDays(-1)
) and deeply nested variables (windowTitle becomes $host.UI.RawUI.WindowTitle
).
There are innovative solutions on the internet that use PowerShell’s debugging facilities to create a breakpoint that changes a variable’s value whenever you attempt to read from it. While unique, this solution causes PowerShell to think that any scripts that rely on the variable are in debugging mode. This, unfortunately, prevents PowerShell from enabling some important performance optimizations in those scripts.
Although we could write our own extensions to make these easier to access, Get-Yesterday
, Get-WindowTitle
, and Set-WindowTitle
feel too insignificant to deserve their own commands.
PowerShell lets you define your own types of variables by extending its PSVariable
class, but that functionality is largely designed for developer scenarios, and not for scripting scenarios. Example 3-4 resolves this quandary by using PowerShell classes to create a new variable type (DynamicVariable
) that supports dynamic script actions when you get or set the variable’s value.
##############################################################################
##
## New-DynamicVariable
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Creates a variable that supports scripted actions for its getter and setter
.EXAMPLE
PS > .\New-DynamicVariable GLOBAL:WindowTitle `
-Getter { $host.UI.RawUI.WindowTitle } `
-Setter { $host.UI.RawUI.WindowTitle = $args[0] }
PS > $windowTitle
Administrator: pwsh.exe
PS > $windowTitle = "Test"
PS > $windowTitle
Test
#>
using
namespace
System
using
namespace
System
.
Collections
.
ObjectModel
using
namespace
System
.
Management
.
Automation
param
(
## The name for the dynamic variable
[
Parameter
(
Mandatory
=
$true
)]
$Name
,
## The scriptblock to invoke when getting the value of the variable
[
Parameter
(
Mandatory
=
$true
)]
[ScriptBlock]
$Getter
,
## The scriptblock to invoke when setting the value of the variable
[ScriptBlock]
$Setter
)
Set-StrictMode
-Version
Latest
class
DynamicVariable
:
PSVariable
{
DynamicVariable
(
[string]
$Name
,
[ScriptBlock]
$ScriptGetter
,
[ScriptBlock]
$ScriptSetter
)
:
base
(
$Name
,
$null
,
"AllScope"
)
{
$this
.
getter
=
$scriptGetter
$this
.
setter
=
$scriptSetter
}
hidden
[ScriptBlock]
$getter
;
hidden
[ScriptBlock]
$setter
;
[Object]
get_Value
()
{
if
(
$this
.
getter
-ne
$null
)
{
$results
=
$this
.
getter
.
Invoke
()
if
(
$results
.
Count
-eq
1
)
{
return
$results
[
0
];
}
else
{
$returnResults
=
New-Object
'PSObject[]'
$results
.
Count
$results
.
CopyTo
(
$returnResults
,
0
)
return
$returnResults
;
}
}
else
{
return
$null
;
}
}
[void]
set_Value
(
[Object]
$Value
)
{
if
(
$this
.
setter
-ne
$null
)
{
$this
.
setter
.
Invoke
(
$Value
);
}
}
}
## If we've already defined the variable, remove it.
if
(
Test-Path
variable
:
\
$name
)
{
Remove-Item
variable
:
\
$name
-Force
}
## Set the new variable, along with its getter and setter.
$executioncontext
.
SessionState
.
PSVariable
.
Set
(
(
[DynamicVariable]
::
New
(
$name
,
$getter
,
$setter
)))