When your flashlight uncovers a smiling nine-foot shark inside a darkened space about the size of an elevator—let's just say that you begin to have multiple sources of input.
KA, handling multiple input parameters from the command line of your script will not require you to wrestle elevator sharks. But if you need to supply multiple values via the command line, and you attempt to do so via the $args automatic variable, you will be greeted with the following error message that warns of a type mismatch. The error does not use the term “type mismatch,” but this is what is meant by the error. It states you are attempting to supply an object array to a string. The computername parameter, it states, requires a string for its input:
Get-WmiObject : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'ComputerName'. Specified method is not supported.
At C:\Users\edwils.NORTHAMERICA\AppData\Local\Temp\tmp774.tmp.ps1:18 char:47
+ Get-WmiObject -Class win32_bios -computername <<<< $args
+ CategoryInfo : InvalidArgument: (:) [Get-WmiObject], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.GetWmiObjectCommand
The error is not caused by the array. The error is caused because the $args automatic variable arrives as a System.Object array. The Get-WmiObject cmdlet will accept an array of computer names to the computername parameter. This is seen here where an array of computer names is supplied directly to the computername parameter and BIOS information is retrieved via the WIN32_Bios WMI class.
PS C:\> Get-WmiObject -Class Win32_Bios -ComputerName localhost,loopback
SMBIOSBIOSVersion : 7LETB7WW (2.17 )
Manufacturer : LENOVO
Name : Ver 1.00PARTTBLx
SerialNumber : L3L4518
Version : LENOVO - 2170
SMBIOSBIOSVersion : 7LETB7WW (2.17 )
Manufacturer : LENOVO
Name : Ver 1.00PARTTBLx
SerialNumber : L3L4518
Version : LENOVO - 2170
There are a couple of ways to solve this issue. The first way is to index into the array and force the retrieval of the computer names. This is shown in the Get-BiosArray1.ps1 script:
Get-BiosArray1.ps1
Get-WmiObject -Class win32_bios -computername $args[0]
The technique of indexing directly into the $args automatic variable works well. The problem is it looks like it would only retrieve the first item in the array, but it in fact retrieves both items. Because Windows PowerShell automatically handles the transition between a single item and multiple items in an array, the technique of indexing into element 0 of the array works whether one or more items are supplied. This issue of the way the Windows PowerShell $args automatic variable handles an array of information can be seen in the StringArgs.ps1 script:
StringArgs.ps1
'The value of arg0 ' + $args[0] + ' the value of arg1 ' + $args[1]
When the StringArgs1.ps1 script is run with the array "string1","String2" supplied from the command line, the entire array is displayed in $args[0] and nothing is displayed for $args[1]. This is shown here:
PS C:\> StingArgs.ps1 "string1","String2"
The value of arg0 string1 String2 the value of arg1
PS C:\>
A better way to handle an array that is supplied to the $args automatic variable is to use the ForEach-Object cmdlet and pipeline the array to the Get-WmiObject cmdlet. This is seen in Get-BiosArray2.ps1:
Get-BiosArray2.ps1
$args | Foreach-Object {
Get-WmiObject -Class win32_bios -computername $_
}
When the Get-BiosArray2.ps1 script is started with an array of computer names from the Windows PowerShell prompt, the following output is displayed:
PS C:\> Get-BiosArray2.ps1 localhost,loopback
SMBIOSBIOSVersion : 7LETB7WW (2.17 )
Manufacturer : LENOVO
Name : Ver 1.00PARTTBLx
SerialNumber : L3L4518
Version : LENOVO - 2170
SMBIOSBIOSVersion : 7LETB7WW (2.17 )
Manufacturer : LENOVO
Name : Ver 1.00PARTTBLx
SerialNumber : L3L4518
Version : LENOVO – 2170
There are two advantages to using the ForEach-Object cmdlet. The first is readability of the code. It meets the principle of “least shock.” When someone reads the code and they see that the script accepts an array for input via the $args variable, they are not surprised when they see the script using the ForEach-Object cmdlet to walk through the array. The other advantage is that the script will work when a single value is supplied for the input.
Unfortunately, if the same approach is tried with the StringArgsArray.ps1 script, the value of the $args array is repeated twice. The StringArgsArray1.ps1 script is seen here:
StringArgsArray1.ps1
$args | ForEach-Object {
'The value of arg0 ' + $_ + ' the value of arg1 ' + $_
}
When the StringArgsArray1.ps1 script is started, the results seen here are displayed:
PS C:\> StingArgsArray1.ps1 "string1","String2"
The value of arg0 string1 String2 the value of arg1 string1 String2
PS C:\>
If you examine the output from the StringArgsArray1.ps1 script, you will see both elements of the $args array displayed. If you modify the StringArgsArray1.ps1 script so that you index into the array that is contained in the $_ automatic variable (which represents the current item on the pipeline), you will be able to retrieve both items from the array. The revised script is called StringArgsArray2.ps1, and it is shown here:
StringArgsArray2.ps1
$args | Foreach-Object {
'The value of arg0 ' + $_[0] + ' the value of arg1 ' + $_[1]
}
When the script is run, the correct information is displayed. This is seen here:
PS C:\> StingArgsArray1.ps1 "string1","String2
The value of arg0 string1 the value of arg1 String2
PS C:\>
Now that is pretty cool huh? Well KA, we have reached the end of another exciting Hey, Scripting Guy! article. Join us tomorrow as we continue looking at handling input to a Windows PowerShell script. If you want a sneak peek of what is coming up, you can always follow us on Twitter or join our Facebook group. If you get stuck while you are working on a script, don't forget the Official Scripting Guys Forum, which we link to from a tab on our home page. If you are having problems finding things on the new Script Center, don’t feel like the Lone Ranger. Keep your eyes out for Script Center 101 in which we will unravel the mysteries of the new site.
Ed Wilson and Craig Liebendorfer, Scripting Guys