Reputation: 611
I've got a cmdlet where I accept a list of strings as a parameter, defined in a c# class like this:
[Parameter(Mandatory = true)]
public List<string> AllowedScopes;
So when I call my command called Add-Client
, how do I provide a list of strings in PowerPhell? I've tried this (each line is a different approach):
-AllowedScopes scope1, scope2, scope3
-AllowedScopes [scope1, scope2, scope3]
-AllowedScopes {scope1, scope2, scope3}
-AllowedScopes {"scope1", "scope2", "scope3"}
But I always just get one entry in my list "AllowedScopes" which contains of the full string entered after the AllowedScopes parameter name.
I cannot find anything about this easily so I guess I'm asking the wrong question.
I can of course have the AllowedScopes parameter be a simple string and then do something like this:
var AllowedScopesAsList = this.AllowedScopes.Split(',').ToList();
But I think that this should be something that's provided by powershell (since it provides so much other useful features keeping me from implementing the whole user-interaction part of my cmdlet)
Edit: Here is the full contents of my cmdlet C#-class:
using System.Collections.Generic;
using System.Management.Automation;
namespace MyCmdlets
{
[Cmdlet("Add", "Client")]
public class AddClient : Cmdlet
{
[Parameter(Mandatory = true)]
public List<string> AllowedScopes;
protected override void ProcessRecord()
{
AllowedScopes.ForEach(a => WriteObject(a));
}
}
}
With this if I try entering the list like one of the answers said:
Add-Client -AllowedScopes @('scope1', 'scope2')
I get this output:
<pre>
scope1
scope2
</pre>
Which is as expected.
But it does not work, if I provide the parameter when being asked for it by PowerShell, like this:
<pre>
PS> <b>Add-Client</b> <kbd>Enter</kbd>
Cmdlet Add-Client at CommandPipelineposition 1
Enter the values for the following parameter:
AllowedScopes[0]: <b>@('scope1','scope2')</b> <kbd>Enter</kbd>
AllowedScopes[1]: <kbd>Enter</kbd>
</pre>
Now the output is this:
@('scope1','scope2')
Even if I enter the scopes there one by one it still results in the list receiving just one child:
<pre>
AllowedScopes[0]: <b>'scope1'</b>
AllowedScopes[1]: <b>'scope2'</b>
AllowedScopes[2]:
</pre>
Output:
<pre>
'scope1' 'scope2'
</pre>
/edit2: I've uploaded a video so you can see how powershell is not behaving as expected (at least I think it's not):
Youtube-Video with powershell-misbehavior
Upvotes: 3
Views: 12874
Reputation: 439193
You appear to have hit a bug that affects Windows PowerShell as of v5.1 and PowerShell Core as of v6.1.0
PowerShell's automatic prompting unexpectedly passes individually entered elements to bind to parameter -AllowedScopes
as a single string that is the concatenation of the individual elements, separated with spaces.
By contrast, passing an array of values from the command line - e.g.,
-AllowedScopes scope1, scope2, scope3
- works correctly.
The bug is triggered by your parameter being defined as List<string>
rather than as string[]
.
Arguably, string[]
is the better choice in this case anyway, so the workaround is to declare your -AllowedScopes
parameter as string[]
(array):
Add-Type -TypeDefinition @'
using System.Collections.Generic;
using System.Management.Automation;
namespace MyCmdlets
{
[Cmdlet("Add", "Client")]
public class AddClient : Cmdlet
{
[Parameter(Mandatory = true)]
// Use string[] instead of List<string>
public string[] AllowedScopes;
protected override void ProcessRecord()
{
foreach (var scope in AllowedScopes) {
WriteObject(scope);
}
}
}
}
'@ -PassThru | ForEach-Object Assembly | Import-Module
# Let PowerShell prompt for -AllowedScopes
# Due to use of string[] rather than List<string>, the individually entered
# elements are now correctly passed as an array.
Add-Client
Note that the bug does not surface if a type other than string
is used for the generic list.
Original answer with general information:
Your code works as designed and -AllowedScopes scope1, scope2, scope3
is indeed the way to pass multiple list elements:
scope1, scope2, scope3
uses ,
, the array-construction operator, to construct a 3-element array that is bound to the -AllowedScopes
parameter and converted to a List<string>
instance.
PowerShell's automatic prompting mechanism for mandatory parameters for which no arguments were passed on the command line works as designed too:
For a collection-typed parameter such as -AllowedScopes
:
If you want to allow users to pass all elements with a single prompt, you must implement custom logic:
Declare -AllowedScopes
as a single string so that user can pass something like
'scope1, scope2, scope3'
Split the string value received into the embedded elements inside your cmdlet.
Upvotes: 6
Reputation: 3236
Try -AllowedScopes @('scope1', 'scope2', 'scope3')
@
construct indicates an array, so @(item,item,item,..)
makes an array of values.
Upvotes: 0