Bookmark and Share

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I have a function that I would like to modify to accept multiple inputs. I have seen examples of a function that uses a single input parameter, but I have not found a Windows PowerShell function that uses two inputs. How can I cause the function to accept two inputs? More importantly, how can I call a function that requires two inputs?

-- AR

 

Hey, Scripting Guy! AnswerHello AR,

Microsoft Scripting Guy Ed Wilson here. Back when I was playing in a rock band, I purchased a music synthesizer. It was an older synthesizer, but was cool nonetheless. The only problem was that it allowed only one note at a time. While fun to play when first purchased, it quickly became boring. My creativity really needed more than one note to express itself.

AR, I totally sympathize with your plight—to write really cool scripts you will at times need more than one input, just as I needed more than one note on my synthesizer. Luckily, you can easily add additional inputs to your functions.  

Image of Windows PowerShell 2.0 Best Practices book

Note: Portions of today's Hey, Scripting Guy! article are excerpted from the Microsoft Press book, Windows PowerShell 2.0 Best Practices by Ed Wilson. This book is now available.

To create a function that uses multiple input parameters, use the Function keyword, specify the name of the function, use variables for each input parameter, and then define the script block within the curly brackets. The pattern is seen here:

Function My-Function($Input1,$Input2)
{
 #Insert Code Here
}

An example of a function that takes multiple input parameters is the Get-DiskSpace function, which is seen in the Get-DiskSpace.ps1 script.

Get-DiskSpace.ps1


Function Get-DiskSpace($drive,$computer)
{
 ([wmi]"\\$computer\root\cimv2:Win32_logicalDisk.DeviceID='$drive'").FreeSpace
}
Get-DiskSpace -drive "C:" -computer "win7-pc"

The Get-DiskSpace.ps1 script begins with the Function keyword, the name of the function, and the two input parameters. The input parameters are placed inside parentheses, as seen here:

Function Get-DiskSpace($drive,$computer)

Inside the curly brackets, the Get-DiskSpace function uses the WMI class accelerator to query the Win32_LogicalDisk WMI class. It connects to the computer specified in the $computer parameter and filters out only the drive that is specified in the $drive parameter. When the function is called, each parameter is specified as –drive and –computer. In the function definition, the variables $drive and $computer are used to hold the values supplied to the parameters.

After the data from WMI is retrieved, it is stored in the $driveData variable. The data that is stored in the $driveData variable is an instance of the Win32_LogicalDisk. This variable contains a complete instance of the class. When the Get-DiskSpace.ps1 script runs, the following output is seen:

Image of output of script

 

When accepting parameters for a function, it may be important to use a type constraint to ensure the function receives the correct type of data. To do this, you place the desired type alias inside square brackets in front of the input parameter. This will constrain the data type, and prevent the entry of an incorrect type of data. Allowable type shortcuts are seen in Table 1.

Table 1Data Type Aliases

Alias

Type

[int]

32-bit signed integer

[long]

64-bit signed integer

[string]

Fixed length string of Unicode characters

[char]

A Unicode 16-bit character

[bool]

True/False value

[byte]

An 8-bit unsigned integer

[double]

Double-precision 64-bit floating point number

[decimal]

A 128-bit decimal value

[single]

Single precision 32-bit floating point number

[array]

An array of values

[xml]

XML objects

[hashtable]

A hashtable object (similar to a dictionary object)

 

In the Resolve-ZipCode function, which is seen in the Resolve-ZipCode.ps1 script, the $zip input parameter is constrained to only allow a 32-bit signed integer for input. Obviously, the [int] type constraint would eliminate most of the world's ZIP and postal codes, but the Web service the script uses only resolves US based ZIP codes, so it is a good addition to the function.

In the Resolve-ZipCode function, the first thing that is done is to use a string that points to the WSDL for the Web service. Next, the New-WebServiceProxy cmdlet is used to create a new webservice proxy for the ZipCode service. The WSDL for the ZipCode service defines a method called the GetInfoByZip method. It will accept a standard United States–based zipcode. The results are displayed as a table. The Resolve-ZipCode.ps1 script is seen here.

Resolve-ZipCode.ps1

#Requires -Version 2.0
Function Resolve-ZipCode([int]$zip)
{
 $URI = "http://www.webservicex.net/uszip.asmx?WSDL"
 $zipProxy = New-WebServiceProxy -uri $URI -namespace WebServiceProxy -class ZipClass
 $zipProxy.getinfobyzip($zip).table
} #end Get-ZipCode

Resolve-ZipCode 28273

 

When the Resolve-ZipCode.ps1 script runs, the following output is displayed:

Image of output of script

 

When using a type constraint on an input parameter, any deviation from the expected data type will generate an error similar to the one seen here:

Resolve-ZipCode : Cannot process argument transformation on parameter 'zip'. Cannot convert value "COW" to type "System
.Int32". Error: "Input string was not in a correct format."
At C:\Users\edwils.NORTHAMERICA\AppData\Local\Temp\tmp3351.tmp.ps1:22 char:16
+ Resolve-ZipCode <<<<  "COW"
    + CategoryInfo          : InvalidData: (:) [Resolve-ZipCode], ParameterBindin...mationException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,Resolve-ZipCode

Needless to say, such an error could be distracting to the users of the function. One way to handle the problem of confusing error messages is to use the Trap keyword. In the DemoTrapSystemException.ps1 script, the My-Test function uses [int] to constrain the $myinput variable to accept only a 32-bit unsigned integer for input. If such an integer is received by the function when it is called, the function will return the string "It worked." If the function receives a string for input, an error will be raised, similar to the one seen above.

Rather than displaying a raw error message, which most users and many IT pros find confusing, it is a best practice to suppress the display of the error message, and perhaps inform the user that an error condition has occurred, and provide more meaningful and direct information that the user can then relay to the help desk. Many times, IT departments will display such an error message, complete with either a local telephone number for the appropriate help desk or even a link to an internal Web page that provides detailed troubleshooting and self-help corrective steps the user can perform. You could even provide a Web page that hosted a script the user could run that would fix the problem. This is similar to the Fix it Web pages Microsoft introduced.

When an instance of a System.SystemException class is created when a system exception occurs, the Trap statement will trap the error, rather than allowing it to display the error information on the screen. If you were to query the $error variable, you would see that the error had in fact occurred and was actually received by the error record. You would also have access to the ErrorRecord class via the $_ automatic variable, which means the error record has been passed along the pipeline. This gives you the ability to build a rich error-handling solution. In this example, the string "error trapped" is displayed, and the Continue statement is used to continue the script execution on the next line of code. In this example, the next line of code that is executed is the "After the error" string. When the DemoTrapSystemException.ps1 script is run, the following output is seen:

error trapped
After the error

The complete DemoTrapSystemException.ps1 script is shown here.

DemoTrapSystemException.ps1

Function My-Test([int]$myinput)
{
 
 "It worked"
} #End my-test function
# *** Entry Point to Script ***

Trap [SystemException] { "error trapped" ; continue }
My-Test -myinput "string"
"After the error"

 

AR that is all there is to using multiple inputs for your functions. That also wraps up Function Week. Join us tomorrow on Christmas Day for Quick-Hits Friday!

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 your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys