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".
You want to simplify the creation of large amounts of repetitive source code or other text.
Use PowerShell’s string formatting operator (-f
) to place dynamic information inside of a preformatted string, and then repeat that replacement for each piece of dynamic information.
Code generation is a useful technique in nearly any technology that produces output from some text-based input. For example, imagine having to create an HTML report to show all of the processes running on your system at that time. In this case, “code” is the HTML code understood by a web browser.
HTML pages start with some standard text (<html>
, <head>
, <body>
), and then you would likely include the processes in an HTML <table>
. Each row would include columns for each of the properties in the process you’re working with.
Generating this by hand would be mind-numbing and error-prone. Instead, you can write a function to generate the code for the row:
function Get-HtmlRow($process) { $template = "<TR> <TD>{0}</TD> <TD>{1}</TD> </TR>" $template -f $process.Name,$process.ID }
and then generate the report in milliseconds, rather than hours:
"<HTML><BODY><TABLE>" > report.html Get-Process | ForEach-Object { Get-HtmlRow $_ } >> report.html "</TABLE></BODY></HTML>" >> report.html Invoke-Item .\report.html
In addition to the formatting operator, you can sometimes use the String.Replace
method:
$string
=
@'
Name is __NAME__
Id is __ID__
'@
$string
=
$string
.
Replace
(
"__NAME__"
,
$process
.
Name
)
$string
=
$string
.
Replace
(
"__ID__"
,
$process
.
Id
)
This works well (and is very readable) if you have tight control over the data you’ll be using as replacement text. If it is at all possible for the replacement text to contain one of the special tags (__NAME__
or __ID__
, for example), then they will also get replaced by further replacements and corrupt your final output.
To avoid this issue, you can use the Format-String
script shown in Example 5-9.
##############################################################################
##
## Format-String
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Replaces text in a string based on named replacement tags
.EXAMPLE
PS > Format-String "Hello {NAME}" @{ NAME = 'PowerShell' }
Hello PowerShell
.EXAMPLE
PS > Format-String "Your score is {SCORE:P}" @{ SCORE = 0.85 }
Your score is 85.00 %
#>
param
(
## The string to format. Any portions in the form of {NAME}
## will be automatically replaced by the corresponding value
## from the supplied hashtable.
$String
,
## The named replacements to use in the string
[hashtable]
$Replacements
)
Set-StrictMode
-Version
3
$currentIndex
=
0
$replacementList
=
@()
if
(
$String
-match
"{{|}}"
)
{
throw
"Escaping of replacement terms are not supported."
}
## Go through each key in the hashtable
foreach
(
$key
in
$replacements
.
Keys
)
{
## Convert the key into a number, so that it can be used by
## String.Format
$inputPattern
=
'{(.*)'
+
$key
+
'(.*)}'
$replacementPattern
=
'{${1}'
+
$currentIndex
+
'${2}}'
$string
=
$string
-replace
$inputPattern
,
$replacementPattern
$replacementList
+=
$replacements
[
$key
]
$currentIndex
++
}
## Now use String.Format to replace the numbers in the
## format string.
$string
-f
$replacementList
PowerShell includes several commands for code generation that you’ve probably used without recognizing their “code generation” aspect. The ConvertTo-Html
cmdlet applies code generation of incoming objects to HTML reports. The ConvertTo-Csv
cmdlet applies code generation to CSV files. The ConvertTo-Xml
cmdlet applies code generation to XML files.
Code generation techniques seem to come up naturally when you realize you’re writing a report, but they’re often missed when writing source code of another programming or scripting language. For example, imagine you need to write a C# function that outputs all of the details of a process. The System.Diagnostics.Process
class has a lot of properties, so that’s going to be a long function. Writing it by hand is going to be difficult, so you can have PowerShell do most of it for you.
For any object (for example, a process that you’ve retrieved from the Get-Process
command), you can access its PsObject.Properties
property to get a list of all of its properties. Each of those has a Name
property, so you can use that to generate the C# code:
$process
.
PsObject
.
Properties
|
ForEach
-Object
{
'Console.WriteLine("{0}: " + process.{0});'
-f
$_
.
Name
}
This generates more than 60 lines of C# source code, rather than having you do it by hand:
Console.WriteLine("Name: " + process.Name); Console.WriteLine("Handles: " + process.Handles); Console.WriteLine("VM: " + process.VM); Console.WriteLine("WS: " + process.WS); Console.WriteLine("PM: " + process.PM); Console.WriteLine("NPM: " + process.NPM); Console.WriteLine("Path: " + process.Path); Console.WriteLine("Company: " + process.Company); Console.WriteLine("CPU: " + process.CPU); Console.WriteLine("FileVersion: " + process.FileVersion); Console.WriteLine("ProductVersion: " + process.ProductVersion); (...)
Similar benefits come from generating bulk SQL statements, repetitive data structures, and more.
PowerShell code generation can still help you with large-scale administration tasks, even when PowerShell is not available. Given a large list of input (for example, a complex list of files to copy), you can easily generate a cmd.exe batch file or Unix shell script to automate the task. Generate the script in PowerShell, and then invoke it on the system of your choice!