What Is That Thing Called?
Recently, I was looking at a friend's new module on GitHub. He is by no means a beginner, but hasn't written many modules. He was creating a module based on commands he had written primarily for himself to simplify some of his daily tasks. He intended to publish the module to the PowerShell Gallery. If you have a module, or even a function, that you use privately, you can get away with a lot of things that you can't do when you publish it. If you are the only person running the command and you know how it behaves, you can bend a lot of the rules that I've been writing about and teaching. But when you are writing PowerShell code for public consumption, you need to be more careful.
One of the things in my friend's code that caught my eye was on the subject of naming. My discussion on the topic led me to look at this from a larger context. There is a big difference between running a command and consuming the output versus formatting the output.
Formatting vs Consuming
When you run a PowerShell function that produces output, the function should write one type of structured object to the pipeline. I've been hammering home this idea for a very long time. You never want to include format cmdlets in the output of your command.
This is bad.
Function Get-FooThing {
[cmdletbinding()]
Param([int]$Count = 5)
Get-Process |
Get-Random -Count $Count |
Select-Object -Property Name, Id, CPU, Handles, WS, StartTime
Format-Table -AutoSize
}
I suppose if this is a private command for my use alone, I would know that all I can do is run the command and view the results. I can't pipe it to another command like Sort-Object
. But this is a bad habit to get into. What if I decide I need the output formatted as a list? I either have to edit the command or write a second version. It is impossible for you to know all the ways that someone might want to consume the output of your command. You can make some educated guesses which might help you define things like property names. But you can't know all the ways that someone might want to use the output. This is why your function should write objects to the pipeline. Let the consumer decide how to consume or format the output.
Get vs Show
In my friend's module, this topic arose around a command using the Show
verb. This is my version of what his command was doing conceptually.
Function Show-FooThing {
[cmdletbinding()]
Param(
[int]$Count = 5,
[string]$Title = 'FooThing Display'
)
$out = Get-Process |
Get-Random -Count $Count |
Select-Object -Property Name, Id, CPU, Handles, VM, WS, StartTime
#don't display any errors if the command isn't found
if (Get-Command Out-ConsoleGridView -ErrorAction SilentlyContinue) {
$out | Out-ConsoleGridView -Title $Title -OutputMode None
}
else {
$out | Out-GridView -Title $Title -OutputMode None
}
}
When you look at the code, can you see the challenge? Even if I accept that all I can do with the output of a Show
command is view the output, which is another issue I'll get to in a moment, if I have Out-ConsoleGridView
and Out-GridView
available, I have no choice regarding which command to use. Sure, you could add a parameter to the function, but then I feel you're re-inventing the wheel. PowerShell already has a pattern of
.
All I really need is an adequate corresponding Get
command. In my friend's module he didn't have that only the show command. The better approach would be to write a Get
command that writes objects to the pipeline and let the end-user decide how to consume the output.
Function Get-FooThing {
[cmdletbinding()]
Param([int]$Count = 5)
Get-Process |
Get-Random -Count $Count |
Select-Object -Property Name, Id, CPU, Handles, WS, StartTime
}
Now I can run Get-FooThing | Out-ConsoleGridView
or Get-FooThing | Out-GridView
or Get-FooThing | Format-Table
or Get-FooThing | Export-Csv
. I can do whatever I want with the output. If I want to provide a way to simplify the process, I could write a control script or function.