как объявить класс в файле ‘.ps1’?

Вопрос или проблема

Я хочу предоставить список разрешённых имен для параметра 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

  1. Порядок объявлений: В PowerShell, вызов param() должен быть первой строкой в файле скрипта. Это ограничивает возможность определения классов и функций до того, как параметры будут объявлены.

  2. Классы и атрибуты: Использование атрибутов, таких как 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 без ошибок и неудобств.

Оцените материал
Добавить комментарий

Капча загружается...