Ravior
Ravior

Reputation: 611

How to enter a list of strings as a parameter?

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

Answers (2)

mklement0
mklement0

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:

  • the elements of the collection are prompted for individually,
  • and you signal the end of the input by pressing Enter once more after having submitted the last element.

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

Kirill Pashkov
Kirill Pashkov

Reputation: 3236

Try -AllowedScopes @('scope1', 'scope2', 'scope3')

@ construct indicates an array, so @(item,item,item,..) makes an array of values.

Upvotes: 0

Related Questions