Вопрос или проблема
Я хочу предоставить список разрешённых имен для параметра name
, чтобы пользователь мог к ним перейти с помощью tab
. Я придумал следующее:
Param(
[ValidateSet([foo])]
[string]$Name
)
$name
Class foo : System.Management.Automation.IValidateSetValuesGenerator{
[string[]] GetValidValues(){
return [string[]] ("cat", "dog", "fish")
}}
При вводе .\myScript.ps1 -name
и нажатии на tab
ничего не предлагается. Запуск .\myScript.ps1
возвращает ошибку:
Line |
3 | [ValidateSet([foo])]
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| Не удается найти тип [foo].
Я могу сделать такую же установку в файле psm1
или просто как стандартную функцию, и это работает. Я подумал переместить класс в начало файла сценария, но это не допускается, так как param()
должен быть первой строкой.
Есть ли способ обойти эту проблему? Я на pwsh 7.4
Нет обходного пути для того, что вы пытаетесь сделать: сценарий PowerShell с параметрами, который использует перегрузку ValidateSetAttribute(Type valuesGeneratorType)
, которая также определяет наследуемый тип IValidateSetValuesGenerator
в том же файле.
Вы можете либо определить функцию в сценарии и вызвать её:
class foo : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return [string[]] ('cat', 'dog', 'fish')
}
}
function Get-Foo {
Param(
[ValidateSet([foo])]
[string]$Name
)
}
Используйте перегрузку ValidateSetAttribute(params string[] validValues)
для оценки во время выполнения:
Param(
[ValidateSet('cat', 'dog', 'fish')]
[string]$Name
)
Импортируйте класс, наследующий IValidateSetValuesGenerator
, перед вызовом сценария:
PS ..\pwsh> . .\FooValidatorClass.ps1
PS ..\pwsh> .\myFooScript.ps1 <TAB>
Или используйте модуль, как вы уже знаете.
Ответ или решение
Чтобы объявить класс в файле .ps1
, который используется для параметров в PowerShell, необходимо учитывать несколько ключевых моментов. Ваша текущая реализация сталкивается с ограничениями PowerShell, касающимися порядка объявления параметров и классов. Давайте подробно рассмотрим правильные подходы к данной задаче.
Основная Проблема
Ваша цель — предоставить набор разрешенных значений для параметра Name
, используя атрибут ValidateSet
, который должен автоматически предлагать допустимые значения при нажатии клавиши Tab
. Однако, вы столкнулись с ошибкой, связанной с тем, что ваша реализация класса foo
не может быть найдена во время выполнения.
Ограничения PowerShell
-
Порядок объявлений: В PowerShell, вызов
param()
должен быть первой строкой в файле скрипта. Это ограничивает возможность определения классов и функций до того, как параметры будут объявлены. -
Классы и атрибуты: Использование атрибутов, таких как
ValidateSet
, требует наличия доступного типа во время декларации параметра. Когда класс находится нижеparam()
, PowerShell не может его найти, что и вызывает ошибку.
Способы Обойти Проблему
Существует несколько подходов для эффективного решения вашей задачи. Вот некоторые из них:
1. Использование класса перед параметром через dot-sourcing
Вы можете вынести класс в отдельный файл и загрузить его перед выполнением основного скрипта. Например:
# FooValidatorClass.ps1
class foo : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return [string[]] ('cat', 'dog', 'fish')
}
}
Затем в вашем основном скрипте:
. .\FooValidatorClass.ps1 # Дот-источник класса
Param(
[ValidateSet([foo])]
[string]$Name
)
2. Использование атрибута ValidateSet
с литеральными значениями
Наиболее простой способ — предоставить статический набор значений напрямую в ValidateSet
:
Param(
[ValidateSet('cat', 'dog', 'fish')]
[string]$Name
)
Этот метод исключает необходимость в отдельном классе и позволяет сразу использовать указанные строковые значения.
3. Определение функции для использования в скрипте
Если вы хотите использовать функционал класса, вы можете определить функцию в скрипте и ссылаться на нее:
class foo : System.Management.Automation.IValidateSetValuesGenerator {
[string[]] GetValidValues() {
return [string[]] ('cat', 'dog', 'fish')
}
}
function Get-Foo {
Param(
[ValidateSet([foo])]
[string]$Name
)
}
Резюме
Классами, содержащими реализацию интерфейсов, таких как IValidateSetValuesGenerator
, можно пользоваться, однако следует помнить о порядке их объявления и местоположении их определения. Чтобы избежать ошибок, обязательно выносите классы в отдельные модули или используйте простые статические наборы значений для параметров. Эффективное использование dot-sourcing и функций также доказало свою эффективность.
Следуя этим рекомендациям, вы сможете добиться желаемого поведения от вашего скрипта PowerShell без ошибок и неудобств.