Почему доступ к атрибутам переменной параметра с Get-Variable работает только первый раз в ISE?

Благодаря замечательным людям в Stackru мы получили очень хороший ответ о том, как извлечь значения, определенные в ValidateSet в пределах Param() предложение скрипта или функции:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String]$Type = 'Startup'
)

(Get-Variable Type).Attributes.ValidValues

Единственное, что меня беспокоит, это то, что этот код работает только в первый раз, когда вы запускаете его в PowerShell ISE. Во второй раз, когда вы запускаете его, не генерируется никакого вывода.

Есть ли обходной путь, чтобы он всегда работал? Мы используем PowerShell 4.0 на Win 7 и Win 2012.

2 ответа

Решение

ТЛ; др

  • Наблюдаемое поведение, возможно, является ошибкой в Windows PowerShell v5.1 / PowerShell Core v6.0-beta.4 - см. Эту проблему GitHub.

  • Чтобы обойти проблему без побочных эффектов, используйте следующий подход:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

$MyInvocation.MyCommand.Parameters['Type'].Attributes.ValidValues

Если есть шанс, что Set-StrictMode -version 2 или выше, или вы используете PSv2, используйте

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

($MyInvocation.MyCommand.Parameters['Type'].Attributes |
  Where-Object { $_ -is [System.Management.Automation.ValidateSetAttribute] }).ValidValues

Дополнительная справочная информация

Проблема не связана с ISE как таковым, но с повторяющимся точечным источником (ISE просто выполняет все сценарии путем точечного поиска их).

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

В данном случае при вызове сценария с точечным источником эффективно добавляется переменная параметра $Type к вызывающей области как регулярной переменной, как задумано.

Ошибка появляется, когда вы снова ставите в точку тот же скрипт (предположим, что скрипт в вопросе присутствует как ./script.ps1:

  • После первого вызова с точечным источником переменная $Type по-прежнему нетронутым относительно его атрибута:

    > . ./script.ps1; (Get-Variable Type).Attributes.Count
    Startup
    Shutdown
    LogOn
    LogOff
    3   # PS implicitly adds 2 add'l attributes behind the scenes
    
  • Когда вы снова создаете точечный источник, атрибуты теряются:

    > . ./script.ps1; (Get-Variable Type).Attributes.Count
    0
    

Прежде всего, это поведение наблюдается только в PowerShell ISE (оно отлично работает снаружи). Это может быть объяснено следующим постом.

Прочитав его, вы увидите, что есть обходной путь:

Param (
    [ValidateSet('Startup', 'Shutdown', 'LogOn', 'LogOff')]
    [String] $Type = 'Startup'
)

(Get-Variable Type).Attributes.ValidValues

# Do your stuff here

Remove-Variable Type
Другие вопросы по тегам