Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Question
Thursday, July 26, 2012 4:46 AM
I'm currently in the process of writing a script that takes advantage of Powershells comment-based help and plan on using Get-Help from within the script itself. This will allow me to have a -Help parameter that then runs Get-Help against the script being run. I have defined a Param section with a parameter for Help which is a string that allows me to specify the Help required ("", Full, Detailed and Examples). I have set validation on the parameter for this also. The problem I have is that I want to be able to specify -Help on the command line with no value and have it use a default of an empty string. When I do this I get the following error:
PS > .\Test.ps1 -Help
C:\Test.ps1 : Missing an argument for parameter 'Help'. Specify a parameter of type 'System.String' and tr
y again.
At line:1 char:17
- .\Test.ps1 -Help <<<<
+ CategoryInfo : InvalidArgument: (:) [Test.ps1], ParameterBindingException
+ FullyQualifiedErrorId : MissingArgument,Test.ps1
PS >
It works if I use -Help ""
A sample showing the issue follows:
<#
.SYNOPSIS
Sample code
#>
param ( [Parameter(Mandatory=$true,ParameterSetName="Backup")]
[switch]$Backup,
[Parameter(Mandatory=$true,ParameterSetName="Help")]
[ValidateSet("","Full","Detailed","Examples")]
[AllowEmptyString()]
[string]
# Specify Full, Detailed or Examples
$Help = ""
)
switch ($PSCmdlet.ParameterSetName) {
Backup {"Backup"}
Help {
switch ($Help) {
Full {Get-Help $MyInvocation.MyCommand.Definition -Full}
Detailed {Get-Help $MyInvocation.MyCommand.Definition -Detailed}
Examples {Get-Help $MyInvocation.MyCommand.Definition -Examples}
default {Get-Help $MyInvocation.MyCommand.Definition}
}
}
}
Am I missing something or is it a bug?
All replies (25)
Thursday, July 26, 2012 9:31 AM ✅Answered | 2 votes
You can pass a Boolean value to any Switch parameter by placing a colon between the parameter name and the value. You can also pass any value that can be converted to Boolean but only to Switch parameters of compiled cmdlets, not those declared in advanced functions.
# compare
Get-ChildItem -Recurse | Measure-Object
# explicitly
Get-ChildItem -Recurse:$false | Measure-Object
# or
Get-ChildItem -Recurse:0 | Measure-Object
To pass a string as a parameter name you would have to expand the command and call it with Invoke-Expression.
To get the functionality I believe you want, you can try adding a dynamic parameter that gets created when the ParameterSetName is Help. In this case I named it Kind, feel free to change it. Then you can get the script’s simple help when it is called without arguments, or just the Help Switch, and the other kind of help by passing the corresponding value to the Kind dynamic parameter.
To test it, call it like this:
C:\YourScript.ps1
C:\YourScript.ps1 -Backup
C:\YourScript.ps1 -Backup:$false
C:\YourScript.ps1 -Help
C:\YourScript.ps1 -Help -Kind full
C:\YourScript.ps1 -Help -Kind detailed
C:\YourScript.ps1 -Help -Kind examples
<#
.SYNOPSIS
Sample code
#>
[CmdletBinding(DefaultParametersetName="Help")]
param (
[Parameter(ParameterSetName="Backup")]
[switch]$Backup,
[Parameter(ParameterSetName="Help")]
[switch]
# Specify the Kind of help, i.e. Full, Detailed or Examples
$Help
)
dynamicparam {
if ($psCmdlet.ParameterSetName -eq 'Help') {
$parameterAttribute = New-Object Management.Automation.ParameterAttribute -Property @{
ParameterSetName = 'Help'
}
$validateSetAttribute = New-Object Management.Automation.ValidateSetAttribute "","Full","Detailed","Examples"
$attributeCollection = New-Object Collections.ObjectModel.Collection[Attribute]
$attributeCollection.Add($parameterAttribute)
$attributeCollection.Add($validateSetAttribute)
$dynParam = New-Object Management.Automation.RuntimeDefinedParameter Kind, String, $attributeCollection
$paramDictionary = New-Object Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add('Kind', $dynParam)
$paramDictionary
}
}
process {
switch ($PSCmdlet.ParameterSetName) {
Backup {
if ($Backup.IsPresent) {
"Backup"
}
}
Help {
switch ($PSBoundParameters['Kind']) {
Full {Get-Help $MyInvocation.MyCommand.Path -Full}
Detailed {Get-Help $MyInvocation.MyCommand.Path -Detailed}
Examples {Get-Help $MyInvocation.MyCommand.Path -Examples}
default {Get-Help $MyInvocation.MyCommand.Path}
}
}
}
}
Friday, July 27, 2012 5:28 AM ✅Answered
zx38,Prior to creating this post and to allow me to progress the code I had done something similar your suggestion by creating a set of additional switches that apply the the Help parameter set as shown below without needing the dynamic parameter.<#.SYNOPSIS Sample code#>[CmdletBinding(DefaultParameterSetName="Help")]param ( [Parameter(Mandatory=$true,ParameterSetName="Backup")] [switch]$Backup, [Parameter(ParameterSetName="Help")] [switch] # Specify the Kind of help, i.e. -Full, -Detailed or -Examples $Help, [Parameter(ParameterSetName="Help")] [switch]$Examples, [Parameter(ParameterSetName="Help")] [switch]$Detailed, [Parameter(ParameterSetName="Help")] [switch]$Full)switch ($PSCmdlet.ParameterSetName) { Backup {"Backup"} Help { if ($Full) {Get-Help $MyInvocation.MyCommand.Path -Full} elseif ($Detailed) {Get-Help $MyInvocation.MyCommand.Path -Detailed} elseif ($Examples) {Get-Help $MyInvocation.MyCommand.Path -Examples} else {Get-Help $MyInvocation.MyCommand.Path} }} This works with the following command lines:C:\Test2.ps1C:\Test2.ps1 -BackupC:\Test2.ps1 -HelpC:\Test2.ps1 -Help -FullC:\Test2.ps1 -Help -DetailedC:\Test2.ps1 -Help -ExamplesIt looks like that's what I'll run with. BigTeddy,The reason I'm looking at including the help logic in the script is so my fellow sysadmins that aren't yet using Powershell for day to day tasks (they live in the GUI world) can use something they're familiar with to obtain help the same way many exe's and vbscripts provide.
Monday, July 30, 2012 1:31 AM ✅Answered | 2 votes
That is happening because the DefaultParametersetName is Help, the potential problem there is an unresolved parameter name (AmbiguousParameterSet), as a result the command will not execute. You can restrict the dynamic parameter creation further by ensuring the Help Switch is present:
# change this line…
if ($PSCmdlet.ParameterSetName -eq 'Help') {
# …to:
if ($PSCmdlet.ParameterSetName -eq 'Help' -and $Help.IsPresent) {
Thursday, July 26, 2012 4:55 AM
To get full help on any cmdlet, you use
Get-Help <cmdletname> -Full
-Help is not a valid switch.
So in your case it would be:
Get-Help .\test.ps1 -Full
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 5:41 AM
Grant,
The issue I have is with getting the param statement in the script to process successfully. -Help is a parameter to the script I'm writing which I then convert to the correct parameter and pass to Get-Help.
Steve
Thursday, July 26, 2012 6:10 AM
Sorry, ignore my previous (deleted) post. I'm still waking up!
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 6:17 AM
This simple answer is that your $help parameter is mandatory, and must be passed a string. Your empty string works. That's how it's meant to work. By calling -Help without any value will pass a $null value to the script. A $null is not the same as an empty string.
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 6:31 AM
But I must just point out that giving a madatory parameter a default is useless and misleading: Because the parameter is mandatory, it will always be specified, and therefore the default will never be used.
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 6:56 AM
Even if I take the mandatory statement out it still gives the error and doesn't use the default value specified.
Thanks
Thursday, July 26, 2012 7:42 AM
If I take the mandatory out, and don't specify the -Help parameter, the default gets applied:
PS C:\scripts> .\ftest.ps1
NAME
C:\scripts\ftest.ps1
SYNOPSIS
Sample code
SYNTAX
C:\scripts\ftest.ps1 -Backup [<CommonParameters>]
C:\scripts\ftest.ps1 [-Help <String>] [<CommonParameters>]
DESCRIPTION
RELATED LINKS
REMARKS
To see the examples, type: "get-help C:\scripts\ftest.ps1 -examples".
For more information, type: "get-help C:\scripts\ftest.ps1 -detailed".
For technical information, type: "get-help C:\scripts\ftest.ps1 -full".
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 8:00 AM
Can you post the code as you have it now?
Running the command with no parameters tells me the Prameter set cannot be resolved using the specified named parameters.
I did read something the other day about PS V3 defaulting to the first parameter set and not needing to specify a DefaultParameterSet on the CMDLetBindings. I'm using PS V2 and no CMDLET bindings at present
Thanks
Thursday, July 26, 2012 8:11 AM
Yes, I'm sorry, I'm testing on a version 3 Powershell. When I tried it on v.2, I get the error: "cannot be resolved using the specified named paramters"
Strange, but true. Try [cmdletbinding(DefaultParameterSetName='Help')]
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 8:16 AM | 1 vote
It is not a bug. When you include a parameter name in the command, PowerShell expects an argument for that parameter to pass to the command, even if the parameter was declared with a default value; the only exception is when the stated parameter is of type Switch. It is worth noting that it is possible to pass arguments to Switch parameters explicitly.
If I understand you objective correctly, I would set the DefaultParameterSetName attribute to Help, which will pick up Help parameter’s default value when the script is called without arguments. The AllowEmptyString attribute is not necessary because you have included an empty string in the ValidateSet attribute. I also adjusted the argument passed to Get-Help by replacing Definition with Path, I believe it is safer, try it.
<#
.SYNOPSIS
Sample code
#>
[CmdletBinding(DefaultParametersetName="Help")]
param (
[Parameter(ParameterSetName="Backup")]
[switch]$Backup,
[Parameter(ParameterSetName="Help")]
[ValidateSet("","Full","Detailed","Examples")]
[string]
# Specify Full, Detailed or Examples
$Help = ""
)
switch ($PSCmdlet.ParameterSetName) {
Backup {"Backup"}
Help {
switch ($Help) {
Full {Get-Help $MyInvocation.MyCommand.Path -Full}
Detailed {Get-Help $MyInvocation.MyCommand.Path -Detailed}
Examples {Get-Help $MyInvocation.MyCommand.Path -Examples}
default {Get-Help $MyInvocation.MyCommand.Path}
}
}
}
Thursday, July 26, 2012 8:35 AM
That almost gets me there. It still doesn't stop someone typing the script name and tabbing through the parameters, seeing -Help and hitting Enter at that point.
How do you pass arguments to switch parameters?
I also discovered earlier today that you can't pass a string to a switch parameter. eg if $Help is "Full" then Get-Help .\test.ps1 -$Help throws and error. If I could do that then I wouldn't need the switch statement.
Thursday, July 26, 2012 8:51 AM
That almost gets me there. It still doesn't stop someone typing the script name and tabbing through the parameters, seeing -Help and hitting Enter at that point.
How do you pass arguments to switch parameters?
I also discovered earlier today that you can't pass a string to a switch parameter. eg if $Help is "Full" then Get-Help .\test.ps1 -$Help throws and error. If I could do that then I wouldn't need the switch statement
You can only pass booleans to a switch parameter.
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 9:42 AM
OK, I knew that one could pass a boolean to a switch parameter, but I was thinking about an unrelated value that would have been used in the script somehow, like "Full", "Examples" etc. These are string values which I thought the OP wanted to pass with the switch parameter. That's what I meant wouldn't work.
There is no real point in supplying a boolean to the switch parameter, (unless using an expression which evaluated to a boolean), at least that I can see.
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 10:01 AM
Yes, I am backtracking! I honestly didn't think of passing booleans when I wrote that, but now that you point it out, you are right, and I was wrong. I'll amend my post to reflect this proviso.
It's not always bad being wrong, because, for example, I'd never thought of using an expression with a switch parameter, so you taught me something new.
But this is a bit off-topic, as I don't think it's helping the OP's problem. Your suggestion of two parameters, one to indicate -Help, and another to specify the kind of help, is a good one.
Grant Ward, a.k.a. Bigteddy
Thursday, July 26, 2012 10:11 AM | 1 vote
Excellent Bigteddy! That, I appreciate well too.
Thanks for being authentic :)
Thursday, July 26, 2012 11:30 AM
While this has been an interesting exercise academically, I don't really see the point of including all this help logic in the script. Commented help in conjunction with Get-Help has all the switch functionality built in.
Users of Powershell would not expect a -Help switch, and would most likely use the conventional method for getting help for your script.
Grant Ward, a.k.a. Bigteddy
Friday, July 27, 2012 5:21 AM
zx38,
Prior to creating this post and to allow me to progress the code I had done something similar your suggestion by creating a set of additional switches that apply the the Help parameter set as shown below without needing the dynamic parameter.
<#.SYNOPSIS Sample code#>[CmdletBinding(DefaultParameterSetName="Help")]param ( [Parameter(Mandatory=$true,ParameterSetName="Backup")] [switch]$Backup, [Parameter(ParameterSetName="Help")] [switch] # Specify the Kind of help, i.e. -Full, -Detailed or -Examples $Help, [Parameter(ParameterSetName="Help")] [switch]$Examples, [Parameter(ParameterSetName="Help")] [switch]$Detailed, [Parameter(ParameterSetName="Help")] [switch]$Full)switch ($PSCmdlet.ParameterSetName) { Backup {"Backup"} Help { if ($Full) {Get-Help $MyInvocation.MyCommand.Path -Full} elseif ($Detailed) {Get-Help $MyInvocation.MyCommand.Path -Detailed} elseif ($Examples) {Get-Help $MyInvocation.MyCommand.Path -Examples} else {Get-Help $MyInvocation.MyCommand.Path} }}
This works with the following command lines:
C:\Test2.ps1
C:\Test2.ps1 -Backup
C:\Test2.ps1 -Help
C:\Test2.ps1 -Help -Full
C:\Test2.ps1 -Help -Detailed
C:\Test2.ps1 -Help -Examples
It looks like that's what I'll run with.
BigTeddy,
The reason I'm looking at including the help logic in the script is so my fellow sysadmins that aren't yet using Powershell for day to day tasks (they live in the GUI world) can use something they're familiar with to obtain help the same way many exe's and vbscripts provide.
Friday, July 27, 2012 6:00 AM
BigTeddy,
The reason I'm looking at including the help logic in the script is so my fellow sysadmins that aren't yet using Powershell for day to day tasks (they live in the GUI world) can use something they're familiar with to obtain help the same way many exe's and vbscripts provide.
I appreciate that your intentions are noble and good. However, I don't think you're doing your fellow admins any favours really, because they may expect similar type of help with other scripts and cmdlets. As we both know, the standard method of getting help is by using Get-Help.
It may be better to teach your admins to use Get-Help, which will enable them to discover more Powershell cmdlets, and thereby learn the new language.
My point is based on the fact that NO other cmdlets, and no scripts that I have seen, have this sort of help built in as a parameter. Your script would be the exception.
Grant Ward, a.k.a. Bigteddy
Friday, July 27, 2012 8:19 AM
Great, at least you got it how you wanted it and you now know the declared default value of a parameter can only be used when the parameter is not included in the command, so it was not a bug :)
The dynamic parameter would be unavailable if the Backup Switch was present. The reason I suggested that approach, was only because you stated It still doesn't stop someone typing the script name and tabbing through the parameters, seeing -Help and hitting Enter at that point.
Friday, July 27, 2012 8:19 AM
Great, at least you got it how you wanted it and you now know the declared default value of a parameter can only be used when the parameter is not included in the command, so it was not a bug :)
The dynamic parameter would be unavailable if the Backup Switch was present. The reason I suggested that approach, was only because you stated It still doesn't stop someone typing the script name and tabbing through the parameters, seeing -Help and hitting Enter at that point.
Monday, July 30, 2012 12:36 AM
I notice that the dynamic parameter exists until the backup switch is present. It's at the end of the list so shift tab brings it up straight away.
Is there a way to hide it until the help switch is selected?
Monday, July 30, 2012 6:25 AM
That fixes it.
It looks like I could spend days playing around with dynamic parameters but the functionality of the script needs to be done first.