November 10th, 2009

Hey, Scripting Guy! How Can I Create a Custom Function?

Bookmark and Share

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I am getting tired of typing the long commands used in Windows PowerShell. I know you Scripting Guys seem to show people always typing commands inside the Windows PowerShell console, but I do not think I like to work that way. It is TOO MUCH TYPING. I would love to be able to shorten some of the commands, but I do not think that is possible.

— DR

Hey, Scripting Guy! AnswerHello DR,

Microsoft Scripting Guy Ed Wilson here, it is a cold day in Charlotte, North Carolina, in the United States. The sky is clear, and the sun is shining, but it’s an autumn sun and is not doing that much good. I even saw some people wearing jackets outside. For the sunny south, 50 degrees Fahrenheit (10 degrees Celsius) is enough to drive the children inside and bring the big wooly mothball-smelling coats outside. I am sipping a cup of aged Earl Gray tea with a cinnamon stick and a dash of warm milk to smooth the flavor. Along with the tea, I have a slice of Fuji apple and Wisconsin sharp cheddar cheese. It is a departure from my normal afternoon snack, but the combination seems to work well with autumn. The neighborhood Halloween decorations persist in some yards of overzealous homeowners in the subdivision, but that has not stopped others from putting out giant blow-up turkeys in anticipation of Thanksgiving. Yet with all that going on outside, the stores are skipping past Thanksgiving in hopes that St. Nick will soon be there. It is a confusing time. Therefore, a confused snack.

DR, I am not confused by your request. I have felt the same way myself. What I generally do is create a custom function that does what I want, and then create an alias for the function. I then add these items to my Windows PowerShell profile. Let’s begin by looking at creating a custom function.

Note: Portions of today’s Hey, Scripting Guy! post are excerpted from the Microsoft Press book, Windows PowerShell 2.0 Best Practices by Ed Wilson. The book is available for pre-order.

Functions provide a nearly endless capability of customization from within Windows PowerShell. The profile is a great place to supply some of this customization. As an example, suppose that when using the Get-Help cmdlet you prefer to see the full article, but you also know that in most cases the article is too long to fit on a single screen; therefore, you pipeline the output to the more function, which provides paging control. If you were looking for information about the Get-Process cmdlet, the command would be the one shown here:

Get-Help Get-Process -Full | more

There is nothing wrong with typing the above command except that, even when paired with tab expansion, it is more than 30 keystrokes. It does not take very long before you get tired of typing such a command. This is a perfect candidate for a function. When naming functions, it is a best practice to use the Verb-Noun naming convention because this syntax will be familiar to users of Windows PowerShell, and you can take advantage of tab expansion. As seen here, I named our function Get-MoreHelp.

Get-MoreHelp.ps1                                                                             

Function Get-MoreHelp()
{
 Get-Help $args[0] -Full |
 more
} #end Get-MoreHelp

The Get-MoreHelp function begins by using the Function keyword to declare the function. After the Function keyword, we specify the name of the function, which in this example is Get-MoreHelp. The empty parentheses are not required after the function name. The parentheses would be used to define parameters, and without any parameters, the parenthesis are not required. I generally include them as an indicator that a parameter could be specified in the position. This is seen here:

Function Get-MoreHelp()

Following the Function keyword, the function opens the code block by using an opening curly bracket. When typing the function I always open the code block with one curly bracket, and immediately on the next line type the closing curly bracket. In this way, I never forget to close a code block. As a best practice, I always include a comment that indicates the bottom curly bracket closes the function. The end comments also are a tremendous help when it comes time to troubleshoot the script because they promote readability and make it easier to understand the delimiters of the function. In addition, if you have a long function that scrolls off the screen, the end comment, with its repetition of the function name, makes it easier to create the alias for the function as well. This is seen here:

{

} #end Get-MoreHelp

The Get-MoreHelp function uses the $args automatic variable to hold the argument that was passed to the function when it is called. Because the Get-Help cmdlet does not accept an array for the name parameter, we use [0] to index into the first element of the $args array. If, as is required, there is only one item passed to the function, the item will always be element 0 of the array. The function passes the –full switched parameter to the Get-Help cmdlet. The resulting help information is passed along the pipeline via the pipe (“|”) symbol. This is shown here:

Get-Help $args[0] -full |


Overriding existing commands

Because it is possible for the Get-MoreHelp function to return more than a single screen of textual information, the function pipes the help information to the more function. Functions are first-class citizens in Windows PowerShell, and they have priority over executables and even native Windows PowerShell cmdlets. Because of this, it is easy to modify the behavior of an executable or cmdlet by creating a function with the same name as an existing one. This is illustrated by the more function. More.com is an executable that provides the ability to return information to the screen one page at a time—it has been available since the DOS days. The more function is used to modify the behavior of more.com. The content of the more function is shown here:

param([string[]]$paths)
if($paths)
{
    foreach ($file in $paths)
    {
        Get-Content $file | more.com
    }
}
else
{
    $input | more.com
}

By looking at the content of the More function, we see that there has been a useful addition to the functionality of more.com. If you supply a path to the More function, it will retrieve the content of the file and pipe the result to the more.com executable. This is shown here:

Image of supplying a path to the More function


Alias the function

When I create utility functions, I generally like to create an alias for the function. This will enable quick and easy access to the function. It is possible to create the function and the alias in the same script, but not within the function definition. The problem is that within the function definition, the function has not yet been created and therefore you cannot create an alias for a function that does not yet exist. But there is nothing wrong with creating the alias and the function in the same script. Interestingly enough, you can create the alias on a line before the function is declared or after the function is declared. Position does not matter.

Get-MoreHelpWithAlias.ps1

Function Get-MoreHelp()
{
 Get-Help $args[0] -full |
 more
} #End Get-MoreHelp
New-Alias -name gmh -value Get-MoreHelp -Option allscope


Loop the array

The $args variable returns an array; we can use this to our advantage and add the ability to pass two or more pieces of information and receive help for each topic. To do this, we use the For statement to loop through the elements of $args. The For statement uses three parameters: the beginning, the destination, and the method of travel. In this example, the variable $i is used to keep track of the position within the array. The variable $i is set equal to 0, and the –le operator (less than or equal to) is used to allow the loop to continue for the number of times that is represented by the count of the number of items in $args. As the loop progresses, the value of $i is incremented by one each time through the loop. The $i++ construction does this. This line of code is seen here:

For($i = 0 ;$i -le $args.count ; $i++)

One small change is required to the line of code that calls the Get-Help cmdlet. Instead of using $args[0], which will always retrieve the first element in the array, we change the 0 to $i. As the value of $i increases for each loop, the Get-Help cmdlet will query the next item in the array. This modified line of code is seen here:

Get-Help $args[$i] -full |

The remainder of the Get-MoreHelp function is the same as previous versions, which were discussed earlier. The complete function is seen in the Get-MoreHelp2.ps1 script.

Get-MoreHelp2.ps1

Function Get-MoreHelp
{
 # .help Get-MoreHelp Get-Command Get-Process
 For($i = 0 ;$i -le $args.count ; $i++)
 {
  Get-Help $args[$i] -full |
  more
 } #end for
} #end Get-MoreHelp
New-Alias -name gmh -value Get-MoreHelp -Option allscope

To run the Get-MoreHelp function, you can use the alias gmh and supply it one or more cmdlet names to get the help. This is seen in the following image where the function code was typed directly into the Windows PowerShell console:

Image of using the gmh alias

 

Well, DR, this should get you started on creating custom functions and aliases. Join us tomorrow as we continue examining ways to customize our Windows PowerShell environment.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post them on the Official Scripting Guys Forum. See you tomorrow. Until then, keep on scripting!

 

Ed Wilson and Craig Liebendorfer, Scripting Guys

 

Author

0 comments

Discussion are closed.