Параметры - Требование некоторых, но не других - Правильное использование наборов параметров
Поэтому я экспериментирую с 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
)