I've been c-sharping a long time. It's been a while since I've PowerShelled. They're very close, but not quite the same. Here are just a few of the things I needed to relearn to accomplish what I was working on most recently.

Loading modules

From "How do I force powershell to reload a custom module?":

As suggested byย wOxxOmย tryย Import-Module ... -Forceย or if that does not work try to explicitly remove it withย Remove-Moduleย and reimport it

And to grab it from within a ps1 file:

Import-Module $PSScriptRoot\Server-Info.psm1 -Force

Function parameters

From "How to get all arguments passed to function (vs. optional only $args)":

$PSBoundParameters gets you all the parameters that were "bound" along with the bound values in a hashtable, it doesn't get you the optional/extra arguments. That is what $args is for. AFAICT the only way to get what you want is to combine the two:

$allArgs = $PsBoundParameters.Values + $args

Passing parameters to functions, including "switch" parameters:

If used, theย paramย statement MUST be the first thing in yourย scriptย orย function:

param (
    [string]$price = 100, 
    [string]$ComputerName = $env:computername,    
    [string]$username = $(throw "-username is required."),
    [string]$password = $( Read-Host -asSecureString "Input password" ),
    [switch]$SaveData = $false
)
write-output "The price is $price"
write-output "The Computer Name is $ComputerName"
write-output "The True/False switch argument is $SaveData"

Calling this script, and setting the switch parameter -SaveData to $TRUE:
.\demo.ps1 -ComputerName "\\server64" -SaveData

or setting -SaveData to $FALSE:
.\demo.ps1 -ComputerName "\\server64" -SaveData:$false

Using positional parameters and pipeline values:

Just to stay with valid PowerShell verbs, I call mineย Write-Feedback.

function Write-Feedback()
{
    param
    (
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$msg,
        [string]$BackgroundColor = "Yellow",
        [string]$ForegroundColor = "Black"
    )
#...

If you want the graduate school version, here's some info on parameter sets.

Backticks and line breaks

Though .NET style "haystack".Replace("needle", "new needle") works, you apparently can't chain those on new lines like this:

$Value = ($Token.Value) `
    .Replace("'", "'") `
    .Replace("&", "&") `
    .Replace(">", ">") `
    .Replace("&lt;", "<")

Instead you have to -Replace over and over.

$Value = ($Token.Value) `
    -Replace("&apos;", "'") `
    -Replace("&amp;", "&") `
    -Replace("&gt;", ">") `
    -Replace("&lt;", "<")

Also note the backticks that allow you to throw a single command over several lines. Here's a great treatment of backtick use:, though let's spoil the ending and admit now they do a great job explaining why you shouldn't use it to have a line of code span several lines (I feel like the author must have one of those double-wide monitors or uses word wrap):

From blogspot.com:

What is the Backtick in PowerShell?

The backtick, backquote or grave accent serves a single purpose in PowerShell: It is the escape character. This is intuitive when you deal with special characters in double quoted strings such as the dollar sign, double quotes, newlines, or even the backtick character itself.

# Consider using single quotes instead
"This `"string`" `$useses `r`n Backticks '``'"

Thatโ€™s it! Thatโ€™s its only job. This escape character can be used outside of double quotes in the same way to escape characters in command lines.

# Never do this in production code
Write-Host `$Hello `" `` 

That last line is hilarious, btw, and there's an even better argument against backtick use just a little further down:

Backticks Donโ€™t Create Line Continuations

โ€œBut you just said they did?โ€ No, I said that the backtick serves the purpose of an escape character. They donโ€™t create a line continuation so much as they escape the newline character which effectively results in a line continuation. This is an important distinction because thisโ€ฆ

# Never do this in production code
Invoke-WebRequest -Uri 'http://get-powershellblog.blogspot.com/' `
    -Method 'GET'

and thisโ€ฆ

# Never do this in production code
Invoke-WebRequest -Uri 'http://get-powershellblog.blogspot.com/' ` 
    -Method 'GET'

Look exactly the same visually. The difference is that the first one is escaping the newline and the second one is escaping a space. The first block will run, the second will have errors.ย  Because they are not visually distinct, to someone new to PowerShell and unaware that the backtick is an escape character, it is possible they may assume that backticks just allow for the line to continue. When they end up with whitespace after the backtick, it causes confusion due to odd behavior or errors.

Well done. They even use "its" correctly in the post. Impressive. I mean, this is part of the reason I set my text editors (hat can) to delete all end of line whitespace -- I'm now trying to think of a use case where you'd want EOL whitespace -- and I don't agree that it makes code harder to maintain. Line breaks can make code much more easy to read and digest when you're reviewing existing code. But all in all this is an excellent post on backticks and PowerShell.

Regardless, the point here is that backticks can't be used to escape newlines in "haystack".Replace("needle1", "replacement1").Replace("needle2", "replacement2") afaict.

Hashtables and [some about] custom objects

Yes, you can add properties to hashtables like a variant/dynamic type, similar to JavaScript. From microsoft.com:

$ageList = @{}
$ageList.Kevin = 35
$ageList.Alex = 9

Just like the examples above, this example adds those keys if they don't exist in the hashtable already.

The easiest way to add custom objects (similar to .NET anonymous objects) might be this:

Surprised no one mentioned this simple option (vs 3 or later) for creating custom objects:

[PSCustomObject]@{
    First = $First
    Last = $Last
    Phone = $Phone
}

But if you wanted to see all the ways to create a new object throughout PowerShell history (archive link:

For starters, the New-Object cmdlet was introduced in PowerShell v1.0 and has gone through a number of changes, while the use of theย PSCustomObject class came later in v3.0. ย For systems using PowerShell v2.0 or earlier, New-Object must be used. ย The key difference between the 2.0 version and 1.0 version from an administrative point of view is that 2.0 allows the use of hash tables. ย For example:

Examples at that page.

From microsoft.com:

Saving to CSV

Struggling with getting a hashtable to save to a CSV is one of the difficulties that I was referring to above. Convert your hashtable to aย pscustomobjectย and it will save correctly to CSV. It helps if you start with aย pscustomobjectย so the column order is preserved. But you can cast it to aย pscustomobjectย inline if needed.

$person | ForEach-Object{ [pscustomobject]$_ } | Export-CSV -Path $path

Again, check out my write-up on using aย pscustomobject.

Also note examples for saving to/from JSON:

$people | ConvertTo-JSON | Set-Content -Path $path 
$people = Get-Content -Path $path -Raw | ConvertFrom-JSON

That's pretty clean.


Ok, here's an important edit: splatting, which is better than backticks for breaking up function calls with too many parameters to view cleanly.

Splatting is when you pass a collection of parameter values to a command using a HashTable or Array like this:

$Params = @{
    Height = 50
    Width  = 50
    Depth  = 50
    Name   = 'My Widget'
    ID     = '10dbe43f-0269-48b8-96cb-447a755add55'
}
New-Widget @Params

The more I read that giant blog post (mentioned above, as well), the more I'm buying in on his best pratices.

Labels: ,