A script that improves on Get-Command and Get-Help
|PowerShell’s Get-Command and Get-Help cmdlets sometimes don’t provide as much usage information as necessary. The author’s Get-Usage script displays usage for more categories of command types than Get-Command or Get-Help.|
Get-Command -Syntax <cmdlet-name>
(where cmdlet-name is the name of a specific PowerShell cmdlet) lets you instantly see exactly how to use a cmdlet.
To illustrate how this command works, let's look at a specific cmdlet. Suppose you want to use the Start-Sleep cmdlet to pause script execution for 2 seconds, but you're uncertain how Start-Sleep works. Some sleep commands take a time to pause in seconds, whereas other commands take a time in milliseconds. If you run the command
Get-Command -Syntax Start-Sleep
you'll get the output that Figure 1 shows. This output is the Start-Sleep cmdlet’s syntax or usage—that is, how to use the command.
Think of a cmdlet’s syntax as a template for using the cmdlet. The syntax doesn't tell you exactly how or why to use a cmdlet, but it does give you the proper form for using the cmdlet.
To read a cmdlet’s syntax information, you need to understand the symbols used in the output. Words preceded by a hyphen (e.g., -Seconds, -Verbose) are parameter names that should be used literally. Words in angle brackets (e.g., <Int32>, <ActionPreference>) describe the type of quantity that should be used. Square brackets surrounding a word (e.g., \[-Seconds\], \[-Verbose\]) tell you that the parameter is optional. (Square brackets used as empty braces—i.e., \[\]—in a value name indicate that the value can be an array.)
The first line of output in Figure 1 begins with Start-Sleep \[-Seconds\] <Int32>, which tells you that when you use Start-Sleep in this form, you must supply the time to sleep as a .NET Int32 value (i.e., a 32-bit integer). The value has the parameter name Seconds, which the square brackets indicate is optional. Because all the other items on the first line are also enclosed in square brackets, you don't need them to run the command successfully. You can run the command in either of the following two forms:
Start-Sleep -Seconds 2
The second line of output, which begins with Start-Sleep -Milliseconds <Int32>, shows the command’s alternative usage. Again, the value must be a 32-bit integer—but the name of the parameter isn't optional. If you want to use milliseconds to specify 2 seconds (which is 2000 milliseconds), you must do so as follows:
Start-Sleep -Milliseconds 2000
Unfortunately, Get-Command doesn't do nearly as good a job of displaying usage information for other commands, including internal commands such as aliases that you’d think PowerShell should be able to handle easily. After I discuss where and why PowerShell's Get-Command -Syntax cmdlet falls short—and why another cmdlet, Get-Help, isn't necessarily better—I'll present a PowerShell script that you can use as a workaround to display usage for more categories of command types.
Why Get-Command -Syntax Doesn't Always Give You Syntax
Sometimes Get-Command -Syntax doesn't give you the information you were expecting. This problem is most easily explained by demonstrating it. Suppose you're using the Get-Process cmdlet and you want to see details about how you can specify which processes to return. You issue the command
Get-Command -Syntax Get-Process
and get the output that Figure 2 shows.
This output provides a detailed explanation of exactly how to use Get-Process. Because Get-Process’s syntax has three distinct possible forms, three syntax lines display. However, if you use the alias for Get-Process (i.e., gps), as follows
Get-Command -Syntax gps
the output returned is simply Get-Process.
What PowerShell returns when you use Get-Command's -Syntax parameter is the definition of the command. Although definition is a fuzzy term, it basically means what PowerShell knows about the command. For external commands such as executables and scripts, PowerShell doesn't really know anything about them other than their location, so PowerShell simply defines these commands by their location. PowerShell doesn't really know anything about the internals of cmdlets, either, but it does know how they’re used—and therefore uses that information as the definition. For aliases, PowerShell simply returns the underlying command the alias points to, which really is a definition. For functions defined in the PowerShell environment, PowerShell shows the complete code within the function—which, again, is arguably a good way to define a function.
Get-Command is really a tool to help you understand what certain commands are and how they work. The -Syntax parameter is an opportunistic addition by the PowerShell team to provide a shortcut for seeing the proper form for using cmdlets. Although you could use the cmdlet Help files to obtain this information, the command definition is more reliable. PowerShell obtains the definition from the cmdlet metadata, which is complete and correct even if the developer didn't provide cmdlet Help or documented the cmdlet incorrectly. This doesn't mean that documentation obtained from Get-Help is necessarily wrong. In fact, Get-Help is the cmdlet to use if you want to understand the concepts behind a particular command. However, Get-Help syntax information has a higher risk of staleness or inaccuracy as compared with Get-Command, which will always give you the correct syntax for using a command.
Whatever the technical explanation is, the output that PowerShell returns is a real issue. If you're zipping along in PowerShell and you request syntax information for an alias but instead receive the full name for the cmdlet, you have to stop and back up. This derailment is an inconvenience at best. It’s also irritating—if you ask for syntax information, you expect to get syntax information.
Another issue is that automatic syntax display is extremely detailed by design. Universal or even just common parameters (e.g., -Debug, -Verbose, -OutBuffer) are constantly cluttering your view of the usage information. Although helpful as a reminder, displaying common parameters makes it more difficult to see the unique characteristics of specific commands.
Using the Get-Usage Script
The Get-Usage script in Listing 1 focuses on improved syntax output for aliases (and cmdlets). I call the script Get-Usage to distinguish it from the standard definitions PowerShell typically returns.
To illustrate how to use Get-Usage, let’s use Get-Process. In addition, we can use the shortened command name usage; PowerShell automatically prepends Get- to command names as part of its search process.
If you run the command
you get the output that Figure 3 shows. This output is the same as the output from running
Get-Command -Syntax Get-Process
with the common parameters removed and with gps shown as the command name used rather than Get-Process.
The command also works correctly if you use the full cmdlet name. For example, entering
returns the output in Figure 4.
If you want the full usage information, Get-Usage has a -Verbose parameter that tells the command not to cut out the common parameters. Either of the following commands will return the full display:
usage gps -Verbose
usage Get-Process -Verbose
How Get-Usage Works
The key to understanding how the Get-Usage script works is in understanding CommandInfo objects. When you run Get-Command on a command name, Get-Command generates CommandInfo objects that fall into different categories of command type, known formally as a CommandType. Commands of every CommandType have a definition, but each command also has a CommandType property that tells you which kind an object is: AliasInfo, FunctionInfo, FilterInfo, CmdletInfo, ExternalScriptInfo, or ApplicationInfo.
The code in Listing 1 simply takes each command name supplied to the script and uses Get-Command to convert the command name into a CommandInfo object (in the variable $cmd). The PowerShell switch structure then determines how to handle $cmd based on whether the CommandInfo object is an AliasInfo object or something else. Because AliasInfo objects always have a ResolvedCommand property that points to the real command, the script drills into the ResolvedCommand property to obtain its definition directly, as callout A in Listing 1 shows. The definition is always a simple string beginning with the cmdlet name, so the script simply replaces the alias with the cmdlet name.
As callout B in Listing 1 shows, the script handles everything else by simply using the definition directly returned for the object. Regardless of the command, the script's switch structure puts the command definition into the $usage variable. Finally, the script simplifies command display by stripping out common parameters, as callout C in Listing 1 shows.
Get-Usage isn't a perfect script for easy, immediate use. It works best just for providing abbreviated usage information for native cmdlets or aliases that point to them. If the final target is a function or an external command, Get-Usage doesn't improve on Get-Command’s syntax display and might even mangle it a bit. However, Get-Usage provides easy, readable usage information about native cmdlets and cmdlet aliases.