Параметры - Требование некоторых, но не других - Правильное использование наборов параметров

Поэтому я экспериментирую с PowerShell и испытываю небольшие затруднения с пониманием параметров. Из того, что я прочитал, если я укажу параметр, который будет находиться в той же позиции, что и другой, но поместит его в отдельный ParameterSet, для PowerShell потребуется только один из этих параметров.

В этом примере это работает как ожидалось -

[CmdletBinding(DefaultParameterSetName='MultiUser')]

Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$Token,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="MultiUser")]
[string]$UsernamesFile,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="SingleUser")]
[string]$SingleUsername,

[Parameter(Mandatory=$False)]
[switch]$SpecialCase
)

Но если бы я хотел расширить это так, чтобы, как указано выше, вы указали токен, а затем вы указали либо одно имя пользователя, либо файлы имен пользователей, но теперь я хотел бы указать группу, в которой будет собираться пользователь.

Теперь давайте предположим, что пользователь должен войти в одну из двух групп, и я не хочу беспокоиться о том, чтобы иметь дело с различными способами ввода пользователем имени группы, поэтому я использую два переключателя. Я только хочу, чтобы пользователь был в одной группе, а не в обеих, поэтому переключатели должны находиться в одной и той же позиции, но в разных наборах параметров (в зависимости от того, что я прочитал и что приведенный выше пример работает).

Итак, мой второй пример выглядит так -

[CmdletBinding(DefaultParameterSetName='MultiUser')]
Param(
[Parameter(Mandatory=$True,Position=1)]
[string]$Token,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="MultiUser")]
[string]$UsernamesFile,

[Parameter(Mandatory=$True,Position=2, ParameterSetName="SingleUser")]
[string]$SingleUsername,

[Parameter(Mandatory=$True,Position=3, ParameterSetName="GroupA")]
[switch]$GroupA,

[Parameter(Mandatory=$True,Position=3, ParameterSetName="GroupB")]
[switch]$GroupB,

[Parameter(Mandatory=$False)]
[switch]$SpecialCase
)

Это не работает, как ожидалось, однако, это дает мне ошибку -

Новые параметры не могут быть найдены

Может ли кто-нибудь объяснить, почему это не работает, и исправить мое понимание параметров PowerShell?

Спасибо!

2 ответа

Решение

Смотрите внизу для объяснения проблемы с подходом ОП.

Чтобы получить именно то, что вы просите, вам нужно использовать следующее:

# - Make sure that parameters are NON-positional unless explicitly marked otherwise.
# - Specify the default parameter set.
[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='MultiUserA')]
Param(

  # Belongs to all parameter sets.
  [Parameter(Mandatory, Position=1)]
  [string]$Token,

  # Mandatory and positional both when combined with -GroupA or -GroupB.
  [Parameter(Mandatory, Position=2, ParameterSetName='MultiUserA')]
  [Parameter(Mandatory, Position=2, ParameterSetName='MultiUserB')]
  [string] $UsernamesFile,

  # Mandatory - but not positional - both when combined with -GroupA or -GroupB.
  [Parameter(Mandatory, ParameterSetName='SingleUserA')]
  [Parameter(Mandatory, ParameterSetName='SingleUserB')]
  [string] $SingleUsername,

  # Mandatory, whether combined with -UsernamesFile or -SingleUsername
  [Parameter(Mandatory, ParameterSetName='SingleUserA')]
  [Parameter(Mandatory, ParameterSetName='MultiUserA')]
  [switch] $GroupA,

  # Mandatory, whether combined with -UsernamesFile or -SingleUsername
  [Parameter(Mandatory, ParameterSetName='SingleUserB')]
  [Parameter(Mandatory, ParameterSetName='MultiUserB')]
  [switch] $GroupB,

  # Belongs to all parameter sets. Non-mandatory by default.
  [switch] $SpecialCase

)

Как вы видете,

  • Вам необходимо определить 4 набора параметров, которые равняются всем комбинациям " пользователь-файл-против-одного пользователя" и "группа-против-группы-В".

  • Вам необходимо назначить каждый параметр нескольким наборам параметров, выбирая соответствующее подмножество.

    • Обратите внимание, что каждый [Parameter(...)] атрибут может указывать только 1 набор параметров, и любые другие атрибуты, которые вы там указываете - Mandatory, Position,... - применять к параметру только в контексте заданного набора параметров.

Когда вы вызываете свой скрипт с -? (или передать его Get-Help) вы увидите результирующую синтаксическую диаграмму:

script.ps1 [-Token] <string> [-UsernamesFile] <string> -GroupA [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> [-UsernamesFile] <string> -GroupB [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> -SingleUsername <string> -GroupB [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> -SingleUsername <string> -GroupA [-SpecialCase] [<CommonParameters>]

Тем не менее, этот подход является необоснованным по следующим причинам:

  • Вы не должны иметь обязательный [switch] параметры, так как они по определению необязательны.

  • Когда вы вызываете сценарий без параметров для интерактивного ввода аргумента, PowerShell не позволит вам указать значение переключателя (нет значений, которые я пробовал работать: нет true, false, 1, 0,... - попробуйте с ./script someToken someFile)

  • ./script.ps1 someToken -SingleUsername someUser выдает общее сообщение об ошибке (Parameter set cannot be resolved using the specified named parameters.) вместо того, чтобы конкретно указать, что либо -GroupA или же -GroupB отсутствует, потому что PowerShell не может знать, имели ли вы в виду набор параметров SingleUserA или же SingleUserB,

    • В отличие от ./script someToken someFile - с подразумеваемым -UsernamesFile - однозначно, потому что MultiUserA это набор параметров по умолчанию, но из-за -GroupA Переключение является обязательным, вы все равно получите запрос на его значение).
  • И последнее, но не менее важное, как указывает Mathias R. Jessen, используя различные взаимоисключающие переключатели (-GroupA против -GroupB) плохо масштабируется, так как добавление большего -Group* Переключатель быстро делает количество комбинаций, каждое из которых должно быть отражено в их собственном наборе параметров, неуправляемым - см. ниже, как этого избежать.


Как указывается в полезном ответе Матиаса Р. Джессена, лучшим подходом является использование одного параметра для целевой группы, который принимает только значение из заданного набора значений, которое [ValidationAttribute] может обеспечить:

[CmdletBinding(PositionalBinding=$False, DefaultParameterSetName='MultiUser')]
Param(

  [Parameter(Mandatory, Position=1)]
  [string] $Token,

  [Parameter(Mandatory, Position=2, ParameterSetName='MultiUser')]
  [string] $UsernamesFile,

  [Parameter(Mandatory, ParameterSetName='SingleUser')]
  [string] $SingleUsername,

  # Single -Group parameter that only accepts values 'GroupA' and 'GroupB'
  # Input validation is case-INsensitive, as usual.
  [Parameter(Mandatory)]
  [ValidateSet('GroupA', 'GroupB')]
  [string] $Group,

  [switch] $SpecialCase

)

Это дает нам следующие синтаксические диаграммы (обратите внимание, что набор допустимых значений для -Group не отражается)

script.ps1 [-Token] <string> [-UsernamesFile] <string> -Group <string> [-SpecialCase] [<CommonParameters>]
script.ps1 [-Token] <string> -SingleUsername <string> -Group <string> [-SpecialCase] [<CommonParameters>]
  • Это уменьшает количество необходимых наборов параметров до 2.

  • Он поддерживает интерактивный ввод имени группы (хотя, к сожалению, предоставление неверного имени прерывает вызов).

  • Если вы опустите -Groupоба ./script.ps someToken someFile и ./script.ps someToken -SingleUserName someUser теперь ведут себя так же: они запрашивают -Group значение.

  • Пока приходится печатать -Group и значение, возможно, немного более громоздко при вызове, чем наличие отдельных переключателей -GroupA а также -GroupB,

    • это более расширяемый подход, если число групп со временем растет,
    • Завершение вкладки работает, чтобы развернуть / перебрать допустимые значения.

Что касается проблем с вашим оригинальным подходом:

  • Как указывает Clijsters, ваша попытка вызвать ./script.ps1 -Token a -UsernamesFile someFile -GroupA не удалось, потому что:

    • PowerShell должен однозначно разрешить данную комбинацию параметров в наборе параметров.

    • -GroupA принадлежиттолько к набору параметров GroupA, в то время как -UsersnameFile принадлежиттолько к набору параметров MultiUserтаким образом, эти параметры являются взаимоисключающими, и PowerShell не может определить, какой набор параметров использовать.

  • Все непереключающие параметры являются позиционными по умолчанию - если вы явно не отключите их с помощью [CmdletBinding(PositionalBinding=$False,...)] - тогда только индивидуальный [Parameter(...)] атрибуты явно помечены Position атрибут становится позиционным.

  • Кроме того, нет смысла делать [switch] Параметр позиционный, так как они по определению непозиционны: вам всегда нужно указывать их имя (однозначно), и это позволяет их размещать где угодно.

Я бы объединил два переключателя и взял бы ввод в зависимости от того, в какую группу вы хотите их включить.

Вы можете использовать ValidateSet Атрибут проверки, чтобы убедиться, что пользователь указывает одну из двух определенных групп

Param(
    [Parameter(Mandatory=$True,Position=1)]
    [string]$Token,

    [Parameter(Mandatory=$True,Position=2, ParameterSetName="MultiUser")]
    [string]$UsernamesFile,

    [Parameter(Mandatory=$True,Position=2, ParameterSetName="SingleUser")]
    [string]$SingleUsername,

    [Parameter(Mandatory=$True,Position=3)]
    [ValidateSet('A','B')]
    [string]$Group,

    [Parameter(Mandatory=$False)]
    [switch]$SpecialCase
)
Другие вопросы по тегам