Managing PowerShell Functions

Most of you probably know I’ve been using PowerShell since its beta days which now means I have about 10 years worth of PowerShell files. I find it hard to throw anything away. Over this time frame my PowerShell profile scripts have also grown, often loading functions that I’ve come up with. The effect has  been that it takes a little bit longer to bring up a new PowerShell console or ISE session. Since deep down I know there are functions I no longer use, I figured it was time for some profile cleanup. The first step was to identify my functions.

First, how many functions am I talking about? PowerShell stores currently defined and active functions in a PSDrive which means it is pretty easy to get a “directory” listing and count.

image

However, some of those functions are defined by Microsoft and other modules.

image

I don’t have any issues with functions defined from a given source. My problem is in that 100 that have no source.  Again some of those are defined by Microsoft which are fine. So I looked at one of them in more detail.

image

I noticed that these functions included a HelpFile. I knew that none of my functions didn’t. With this information I can get a directory listing and filter with Where-Object.

image

These are functions that are loaded from dot sourcing a ps1 file. By the way, the list had 62 items.

As an alternative I could use Get-Command which would get rid of any filters

Know that this will search for functions in modules not currently loaded so it takes a bit longer to run. This gave me 61 items.

But now at least I can go through the list and identify functions that I haven’t used in years or are now obsolete. Unfortunately, there’s no way to tell from the Function PSDrive or with Get-Command where the function came from.   Luckily I keep all my files in a single drive and most likely the function will be defined in a .ps1 script. I can use Select-String to find it.

In my list there is a function called Show-Balloon that I probably haven’t used in years. What file is it in?

I’m wrapping the function name in a regular expression word boundary so that I don’t get a match on something like show-balloontip. The –List parameter gives me the first match which is all I really need.

image

Because I write all of my functions with the same format, I could have searched like this and gotten the same result.

The output from Select-String is an object which includes a property for the filename or path.

image

Now I can put it all together.

I don’t want to have to re-list all the script files for every function, so I’ll get it once and then use that variable in the Select-Object expression.

image

With this information I can modify my PowerShell profiles and remove the lines that are loading the functions I don’t want or edit the source file to comment out the function if there was something else in the same file I did want to keep.  Any function in this list without a matching source is most likely defined directly in the profile script or will require a manual search.

Time now for a little housekeeping, although I’d love to hear how you keep things tidy in your PowerShell world.

Get PowerShell Parameter Aliases

magnifying-glass During a recent PowerShell training class we naturally covered aliases. An alias is simply an alternate name, often something that is shorter to type, or maybe even more meaningful. There are aliases for commands, properties and parameters. Discovering aliases for commands is pretty easy with Get-Alias. Property aliases are discoverable using Get-Member. But, discovering parameter aliases is a bit more difficult. The information is there, but doesn’t surface very well. It would be terrific if help showed parameter aliases but it rarely does. So here are some ways you might find parameter aliases.

One way is to use Get-Help.

But you need to use Get-Help. If you use the Help function it won’t work. In the command I filtered out parameters that didn’t have aliases. You can also pipe Get-Command to Get-Help.

But for some reason, this doesn’t always work. There are aliases for Get-Service, but these same commands fail to show it.

I know there are aliases because Get-Command shows me, although it takes a little work to extract this information.

Plus I can verify at the prompt:

Since it appears I can always get the information from Get-Command, I wrote a function called Get-ParameterAlias.

The function can take a command name or you can pipe something from Get-Command.

Because parameter information from Get-Command includes common parameters such as -ErrorAction, I’ve skipped those by default, unless you use the -IncludeCommon parameter.

Now it is easy to discover parameter aliases for say a module.

get-parameteralias

Knowing parameter aliases can make you more efficient in the console. But remember, when committing PowerShell to a script use the full parameter name as some of these aliases can be a bit cryptic.

As always, I hope you’ll let me know what you think.

Browsing PowerShell Commands

Whenever I’m exploring a new PowerShell module or snapin, one of the first things I do is list all of the commands found within the module.

You can specify either a module or a snapin. Use the -module parameter for both. However, for larger modules, I’ve realized I need a better way to browse the commands. For example, I might need to see them organized by verb or noun. That information is included with the Get-Command expression. I simply have to ask for it. Here’s a more thorough command and the result.

get-command-mod

I included the command type because some modules might contains cmdlets and functions. I could revise this expression and insert a sort command. But that’s too much typing. Especially if I want to sort and re-sort. Instead, I’ll pipe this command to Out-Gridview.

get-command-mod-ogv

Now I have a sortable and filterable view of all the commands. Plus, it is in a separate window so I have my prompt back and get help for listed commands. I even decided to build a quick-and-dirty function.

Because the module or snapin name can include a wildcard, I added the module name to the output. Now I have a tool to grab all the commands from a module or set of modules and I can filter and browse all I want without having to retype or revise commands at the prompt.

Friday Fun PowerShell Commands by Noun

One of PowerShell’s greatest strength’s is discoverability. Once you know how, it is very easy to discover what  you can do with PowerShell and how. One reason this works is because PowerShell commands follow a consistent verb-noun naming convention. With this in mind, you can see all of the commands organized by noun.

This will work in both v2 and v3.

get-command-noun-01In PowerShell 3.0, this will display commands from all modules, even those not currently loaded. If you want to limit your display to only those modules currently imported use this:

Or you can use this same idea to organize cmdlets in a specific module.

get-command-noun-02Now that you know what you can do, go forth and do it!

Create an HTML PowerShell Help Page

Yesterday I posted an article about getting the online url for a cmdlet help topic. Today I want to demonstrate how we might take advantage of this piece of information.

Since the link is already in the form of a URL, wouldn’t it make sense to put this in an HTML document? At first glance, you might take the command from yesterday and pipe it to ConvertTo-HTML.


Get-Command -CommandType cmdlet | Get-Help |
Select Name,Synopsis,@{Name="URI";Expression={
($_.RelatedLinks | select -ExpandProperty NavigationLink | where {$_.uri}).uri}} |
Where {$_.URI} | ConvertTo-HTML -Title "Help Links" | Out-File c:\work\pshelp.htm

This code will work just fine. But if you look at the resulting file, we don’t have a link. I suppose it would be nice if ConvertTo-HTML could auto-detect URLs and automatically add a link, but it looks like we’ll have to do it. We could probably use a custom hash table to insert the HTML Anchor tags so here’s the first attempt:


Get-Command -CommandType cmdlet | Get-Help | Where {$_.RelatedLinks} |
Select Name,Synopsis,@{Name="URI";Expression={
#add the link tags as part of the output!
$link=$_.RelatedLinks | select -ExpandProperty NavigationLink | where {$_.uri}
if ($link.uri) {
$uri=$link.uri
Write "$uri"
}
else {
#no link so write a null
write $Null
}
}} | Where {$_.URI} | ConvertTo-HTML -Title "Help Links" |
Out-File c:\work\pshelp.htm

One additional change I made was to filter out cmdlets with no related links. Then in the expression scriptblock I can create a new value based on the URI value if it exists. But there is a problem with this, which you’ll see immediately if you run this code. ConvertTo-HTML sees the value of my new URI property and escapes the < and > characters.

That doesn’t help. But this is where the fact that ConvertTo-HTML only creates HTML code because we can parse the code and do a simple replace. Let me jump ahead and pull part of the finished dessert from the oven.


Function Convert-HTMLEscape {

<# convert < and > to < and >
It is assumed that these will be in pairs
#>

[cmdletbinding()]

Param (
[Parameter(Position=0,ValueFromPipeline=$True)]
[string[]]$Text
)

Process {
foreach ($item in $text) {
if ($item -match "<") {
<# replace codes with actual symbols This line is a shortcut to do two replacements with one line of code. The code in the first set of parentheses revised text with "<". This normally gets written to the pipeline. By wrapping it in parentheses it tells PowerShell to treat it as an object so I can then call the Replace() method again and add the >.
#>
($item.Replace("<","<")).Replace(">",">")
}
else {
#otherwise just write the line to the pipeline
$item
}
}
} #close process

} #close function

This function takes string input, presumably from ConvertTo-HTML and replaces the HTML escapes with the “real” characters. This I can use to write to the file.


...| Where {$_.URI} | ConvertTo-HTML -Title "Help Links" | Convert-HTMLEscape | Out-File c:\work\pshelp.htm

One of the reasons I put this in a function is to make it re-usable for future projects where I might need to escape these characters again. We’re getting closer. One last thing before we ice the final dessert: I have been using an expression to get all cmdlet help. But I like flexibility. What if tomorrow I only want a page with New* cmdlets from PowerCLI? So once again, I took my code that works just fine from a prompt into a more flexible and re-usable function.

Function Get-HelpUri {

[cmdletbinding()]

Param(
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a cmdlet name",
ValueFromPipeline=$True,ValueFromPipelinebyPropertyName=$True)]
[ValidateNotNullorEmpty()]
[string]$Name
)

Process {
Write-Verbose "Processing $name"
Get-Help $name | Where {$_.RelatedLinks} |
Select Name,Synopsis,@{Name="URI";Expression={
#add the link tags as part of the output!
$link=$_.RelatedLinks | select -ExpandProperty NavigationLink | where {$_.uri}
if ($link.uri) {
$uri=$link.uri
Write "$uri"
}
else {
#no link so write a null
write $Null
}
}}
} #close process

} #close function

This will write a custom object with the cmdlet name, synopsis and URI property with the HTML code. I still need to convert to HTML and then fix the tags but I can verify it works.


PS C:\> get-command new-object | get-helpuri | ConvertTo-Html |Convert-HTMLEscape



HTML TABLE

Name Synopsis URI
New-Object Creates an instance of a Microsoft .NET Framework or COM object. http://go.microsoft.com/fwlink/?LinkID=11335
5


The icing is to include some style via a CSS file. Here’s a short script on how I might build a file for all of the cmdlets in my current session.


Write-Host "Building cmdlet help report" -ForegroundColor Green

#the file to create
$file="c:\work\cmdletonline.htm"

#be sure to change the path to the CSS file if you want to use it
$cssPath="c:\scripts\blue.css"

#optional image
$imagePath="c:\work\talkbubble.gif"

#some pre content
$preContent=@"



Cmdlet Online Help




"@

#some post content
$postContent=@"

Help for cmdlets found on $env:computername on $(Get-Date)
"@

#a title for the report
$Title="Cmdlet Help"

<# Get all cmdlets in the current session, send them to the Get-HelpURI function to parse out help URLS, filter out those without a link, pass the remaining to the Convertto-HTML to generate HTML code which is piped to my function to replace < with > and the final HTML code is piped to
a file.
#>
Get-Command -CommandType cmdlet | Get-HelpURI | Where {$_.URI} |
ConvertTo-Html -PreContent $PreContent -PostContent $postContent -Title $Title -cssUri $cssPath |
Convert-HTMLEscape | Out-File -FilePath $file -Encoding ASCII

Write-Host "Finished. See $file for the results" -ForegroundColor Green

And here’s the final result in Internet Explorer.

Ok, maybe you don’t have a compelling need for this exact script, but I hope you picked up on the importance of writing code for re-use and taking advantage of the pipeline.

If you’d like to try my code out for yourself, including the graphic and CSS file, download this zip file.