PowerShell Problem Solver: Finding Orphan Aliases
Efficiency is paramount when working with the PowerShell console to get work done. Because there’s a lot of typing involved when working with PowerShell, there’s several features that intend to limit typing, such as tab completion for command and parameter names. Another ease-of-use feature is the PowerShell alias. Instead of typing Get-WmiObject, you can type a shortcut, gwmi. There’s nothing wrong with that. When it comes time to writing a script, however, the best practice is to use full cmdlet and parameter names. Another use of aliases is as transition-aids, which is demonstrated by using dir instead of having to use Get-ChildItem.
PowerShell automatically defines many aliases whenever you start a new session. You can also create your own aliases. You might do this for your own functions or even command-line tools. In my PowerShell profile, I have a command like this:
1 |
Set-alias –name np –value notepad.exe |
At any time, I can type np and Notepad will launch. My PowerShell profile has grown over the years, and it has been moved around as I have changed computers. Recently I realized I had some aliases that were pointing to items that no longer existed. It doesn’t really matter if you have orphaned aliases, but I like things neat, so I decided that I needed a tool to test if an alias was still valid. I can use the Get-Alias cmdlet to get details.
As I expected np leads to notepad.exe. Let’s peak behind the curtain and see if there is anything else I can use.
Excellent. The definition property gives me the complete command. This is important because I can use Get-Command to verify it.
1 2 |
$a = Get-alias np get-command -Name $a.Definition |
That looks good and should mean that notepad.exe exists. But let’s test with something that I now will fail. I’ll create an alias that points to a non-existent command.
1 |
set-alias -name foo -value c:\windows\system32\foobar.exe |
PowerShell will happily create the alias.
But let’s test for the backing command.
I get an exception. That is useful, because now I know I can use a try-catch statement.
1 |
Try { $c = Get-Command $b.Definition -erroraction Stop;$True} Catch { $false} |
This is a quick and dirty proof of concept.
Now I have something that I can wrap into a function to test an alias. Here’s my finished Test-Alias command.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
#requires -version 4.0 Function Test-Alias { <# .Synopsis Test if an alias is valid. .Description Use this command to verify that an alias is pointing to a command that still exists. Use -Quiet to get only a True/False result. NOTE: Using this command will have a side-effect of importing associated modules. If the alias points to a command in a PSSnapin, you will get an incorrect result unless you add the PSSnapin first. .Parameter Name The alias name. You can pipe Get-Alias to this command. .Parameter Quiet Only display True or False. .Example PS C:\> test-alias gsv,ps,foo,np | format-table -AutoSize Name Definition Test ---- ---------- ---- gsv Get-Service True ps Get-Process True foo x False np C:\windows\notepad.exe True .Example PS C:\> get-alias | test-alias | where {! $_.Test} | format-table -AutoSize Name Definition Test ---- ---------- ---- chr C:\Users\Jeff\AppData\Local\Google\Chrome\Application\chrome.exe False foo x False pe G:\sysinternals\procexp.exe False TryMe Get-Foo False Find aliases that won't work. .Example PS C:\> test-alias gci -quiet True A simple boolean test .Notes Last Updated: 4/2/2015 Version : 1.0 Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/ **************************************************************** * DO NOT USE IN A PRODUCTION ENVIRONMENT UNTIL YOU HAVE TESTED * * THOROUGHLY IN A LAB ENVIRONMENT. USE AT YOUR OWN RISK. IF * * YOU DO NOT UNDERSTAND WHAT THIS SCRIPT DOES OR HOW IT WORKS, * * DO NOT USE IT OUTSIDE OF A SECURE, TEST SETTING. * **************************************************************** .Link Get-Alias Get-Command .Inputs [string] .Outputs [Boolean] [PSCustomObject] #> [cmdletbinding()] Param( [Parameter(Position=0,Mandatory,HelpMessage="Enter the name of an alias", ValueFromPipeline,ValueFromPipelineByPropertyName)] [ValidateNotNullorEmpty()] [string[]]$Name, [switch]$Quiet ) Begin { Write-Verbose "Starting $($MyInvocation.Mycommand)" } #begin Process { foreach ($alias in $Name) { Write-Verbose "Testing alias : $alias" Try { $def = (Get-Alias -Name $alias -ErrorAction Stop).Definition } Catch { Write-Warning "No alias found called $alias" } if ($def) { Try { Write-Verbose "Verifying command: $def" if (Get-Command -Name $def -erroraction Stop) { $tested = $True } } Catch { $tested = $False } if ($Quiet) { Write $tested } else { #write a custom object to the pipeline [pscustomobject]@{ Name = $alias Definition = $def Test = $Tested } } #clear $def so it doesn't get accidentally re-used Remove-Variable -Name Def } #if $def } #foreach } #process End { Write-Verbose "Ending $($MyInvocation.Mycommand)" } #end } #end Test-Alias #create an alias Set-Alias -Name ta -Value Test-Alias |
You can test an alias by name.
Or you can pipe names to it.
The default behavior is to write a custom object to the pipeline, but I also thought there might be a time when you want to test and need a simple Boolean result. Test-Connection works the same way.
If you were scripting with my function, then this means you could write a simple if statement like this:
1 2 3 |
If (test-alias ls) { #found } |
But I don’t need a script to validate all of my aliases. I can do it from the console.
1 |
get-alias | test-alias | where {-Not $_.Test} | format-table –AutoSize |
Now I know what to clean up. Although I can’t tell where these aliases were defined, if you want to test aliases defined in your profile, start a new PowerShell session and run Test-Alias.
There are a few caveats with Test-Alias. Because it uses Get-Command, if you have an alias that points to a command in a module that isn’t currently loaded in your session, that module will be loaded during the process of using Test-Alias.
As an example, let’s say I have an alias called gau that points to Get-ADUser from the ActiveDirectory module. PowerShell will load the ActiveDirectory module during the process of testing the alias. This is because of PowerShell’s autoloading feature introduced in PowerShell 3.0. This also means that any alias that point to commands in a PSSnapin, will fail to properly resolve, unless you first manually add the PSSnapin.
Even if you don’t need to test aliases, I hope you learned a few thing about creating PowerShell tools. As always I’d love to hear what you think.