Что именно означает Видимость Приватной?

#powershell #scope #invocation

#powershell #область применения #призыв

Вопрос:

Я записываю следующее в модуль сценария (т. Е. Файл psm1) и выполняю Import-Module для этого файла:

 New-Variable -Name 'mytest' -Value 'Foo' -Scope 'Global'
 

Теперь я могу получить доступ к этой переменной в моем сеансе PowerShell. $mytest Foo например, выдает.

В качестве альтернативы я записываю следующее в модуль сценария и выполняю Import-Module для этого файла:

 New-Variable -Name 'mytest' -Value 'Foo' -Scope 'Global' -Visibility Private
 

Теперь переменная $mytest больше не доступна в моем сеансе PowerShell, что вполне справедливо.

Но если я это сделаю . {$mytest} , я снова смогу получить доступ к моей «частной» переменной — как это происходит? На всякий случай, если вы спросите, amp; {$mytest} работает то же самое, кстати.

Примечание 1: Пожалуйста, имейте в виду, что я спрашиваю не о (псевдо-) закрытой области видимости, а о параметре видимости private, в чем разница: частная область видимости блокирует видимость «вниз по иерархии вызовов», тогда как параметр видимости private должен блокировать видимость «вверх по иерархии вызовов» (по крайней мере, я понял это именно так).

Примечание 2: Глобальная область в приведенном выше примере существует только для того, чтобы иметь возможность получить доступ к переменной $mytest из сеанса PowerShell с первой попытки. В противном случае даже «свободные» общедоступные переменные в модуле («свободные» здесь означают переменные вне всех скриптовых блоков) не были бы доступны из сеанса PowerShell, за исключением случаев, когда вы будете использовать Export-ModuleMember -Variable 'mytest' (тогда вам также придется явно экспортировать все остальное из модуля). Но это ничего не изменит в результате.

Ответ №1:

Хорошая новость: я не думаю, что функция переменной видимости актуальна на практике, по крайней мере, не для кода PowerShell, который выполняется в обычном сеансе PowerShell (обычно в консоли (терминале)).).

На момент написания этой статьи документация сбивает с толку и кажется несовместимой с фактическим поведением.

about_Scopes: рассказывает о том, как видимость относится к контейнерам, а не к областям (курсив добавлен):

Свойство видимости переменной или псевдонима определяет, можно ли видеть элемент за пределами контейнера, в котором он был создан. […] Контейнер может быть модулем, сценарием или оснасткой. Элементы, которые имеют частную видимость, можно просматривать и изменять только в контейнере, в котором они были созданы. Если контейнер добавлен или импортирован, элементы, которые имеют частную видимость, не могут быть просмотрены или изменены «.

  • Оснастки были предшественниками модулей только для двоичных файлов из версии v1 и больше не актуальны.
  • Скрипты не имеют состояния, кроме как во время их выполнения, поэтому идея доступа к его переменным извне обычно неприменима (см. Ниже «Поиск точек»).
  • Модули имеют состояние, но все переменные в модуле по умолчанию скрыты извне, без необходимости устанавливать специальные свойства переменных. И наоборот, вы должны явно запросить экспорт переменной через Export-ModuleMember -Variable (а также через VariablesToExport запись в соответствующем манифесте модуля, если таковой имеется).
    • Примечание: Для функций логика отличается, поскольку — в отличие от переменных — все функции экспортируются по умолчанию. Чтобы сделать функции действительно закрытыми, то есть скрыть их от пользователей вашего модуля, вы должны переключиться на явный экспорт: если у вашего модуля нет манифеста, используйте Export-ModuleMember -Function ... вызов в нижней части вашего скриптового модуля, чтобы выбрать функции для явного экспорта. Если у вас есть манифест (вспомогательный *.psd1 файл), достаточно перечислить интересующие функции в FunctionsToExport записи. Обратите внимание, что Export-ModuleMember и FunctionsToExport запись в манифесте модуля принимают шаблоны имен (подстановочные знаки); таким образом, например, если вы примете соглашение об использовании соглашения об именовании глаголов и существительных только для общедоступных функций в вашем модуле, FunctionsToExport = @( '*-*' ) запись автоматически ограничит экспорт функциями, неявно обозначенными как общедоступные.

Поэтому все потребности в видимости должны быть удовлетворены даже без свойства видимости (установлено значение Private ):

  • Как указано, модули скрывают свои переменные по умолчанию, но разрешают явный экспорт; аналогично, то, какие функции видны извне, можно контролировать с помощью явного экспорта (любые функции, которые вы не экспортируете, неявно скрыты, хотя без явного экспорта все видны).
  • Когда скрипт или функция выполняются, они могут скрывать свои переменные из дочерних областей с помощью Private опции / «scope»; например:
       $private:foo = 'bar' # create variable only visible in the very same scope
      "in same scope: [$foo]"        # -> 'in same scope: [bar]'
      amp; { "in child scope: [$foo]" } # -> 'in child scope: []'
     
  • Если сценарий предназначен для точечного поиска, то есть для загрузки непосредственно в текущую область с помощью . оператора, и вы выборочно хотите скрыть переменные, созданные в нем, самым простым решением является включение таких переменных amp; { ... } , чтобы создать их в дочерней области, которая отбрасывается при выходе из блока сценария; в качестве альтернативы, но менее надежно, вы также можете выполнить явную очистку с помощью Remove-Variable

Что касается принудительного применения свойства видимости, начиная с PowerShell 7.1:

Точечный поиск сценария из глобальной области видимости, по-видимому, является единственным сценарием, в котором невидимые переменные не могут быть доступны напрямую в области вызывающего объекта.

Запуск такой команды, как
New-Variable -Name 'mytest' -Value 'Foo' -Scope 'Global' -Visibility Private непосредственно в интерактивном приглашении равносильно размещению его в скрипте и точечному поиску этого скрипта.

Попытка прямого доступа к такой переменной в глобальной области видимости приводит к ошибке завершения инструкции: PermissionDenied: Cannot access the variable '$mytest' because it is a private variable.

Как вы заметили, это ограничение тривиально обходится путем доступа к переменной через блок сценария, будь то as . { $mytest } или as amp; { $mytest } — это работает и со скрытыми командами.

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

Я не знаю, каков был первоначальный замысел проекта, но:

  • Я подозреваю, что мы имеем дело с ошибкой, которая не позволяет последовательно применять невидимость.
  • Любопытно, что когда она применяется, невидимая переменная не просто обрабатывается так, как будто ее не существует, но вместо этого сообщается об ошибке; напротив, невидимая команда действительно представляется так, как будто ее не существует.[1]
  • Документация функции, относящаяся к свойству commands, предполагает, что функция предназначена для [System.Management.Automation.CommandInfo] доступа .Visible из рабочей области (курсив добавлен).

    • «Указывает, должна ли команда быть разрешена для выполнения запросом, внешним по отношению к пространству выполнения.«
    • Подразумевается, что видимость не предназначена для использования внутри сеанса, учитывая, что обычный сеанс PowerShell ограничен одним пространством выполнения, за исключением случаев, когда вы явно создаете дополнительные пространства выполнения с помощью PowerShell SDK.



[1] Как вы обнаружили, можно также управлять видимостью команд, включая функции, а именно с помощью [System.Management.Automation.CommandInfo] представления команды, например, возвращаемого Get-Command командлетом; например: (Get-Command myFunc).Visibility = 'Private'

Комментарии:

1. «Поэтому все потребности в видимости должны быть удовлетворены даже без свойства Private visibility» — я должен признать, что изначально у меня было более сложное намерение, но я хотел начать с упрощенного вопроса, чтобы избежать слишком большого количества встречных вопросов. Мое первоначальное намерение состояло в том, чтобы сделать функции модуля видимыми-частными, чтобы иметь вспомогательные функции в модуле, которые могут произвольно обращаться друг к другу, но не видны снаружи. К сожалению, вы достигаете прямо противоположного с помощью области private: она блокирует межфункциональный доступ, но по-прежнему разрешает доступ импортеру.

2. Просто на тот случай, если вам интересно, как я сделал видимость функций закрытой (потому что, к сожалению, нет командлета New-Function ): после определения функции Test-Function я поместил следующую команду в конец модуля: (Get-Command -Name 'Test-Function').Visibility = [System.Management.Automation.SessionStateEntryVisibility]::Private .

3. Я хочу избежать командлета Export-ModuleMember или FunctionsToExport , соответственно, потому что вам приходится адаптировать его каждый раз, когда вы добавляете (экспортируемую) новую функцию в модуль. Видимость private , согласно ее официальному описанию, должна быть идеальной для моей цели (и, возможно, даже для причины ее существования), но я обнаружил, что она работает не так, как ожидалось. И поэтому я хотел знать, почему.

4. Функции, которые не экспортируются, не являются частными. Они просто не экспортируются. В этом и есть разница. И это имеет значение, если вы попытаетесь решить ту же проблему при импорте файла ps1 вместо файла psm1.

5. «Когда это применяется, любопытно, что невидимая переменная не просто обрабатывается, как если бы она не существовала, но вместо этого сообщается об ошибке». — Интересно, что частная функция , к которой вы безуспешно пытаетесь получить доступ, обрабатывается так, как если бы она не существовала.


Ответ №2:

Вы хотите сказать, что справочная информация в новой переменной файла справки bulti-in не ясна:

https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/new-variable?view=powershell-7.1

-Видимость

Определяет, видна ли переменная за пределами сеанса, в котором она была создана. Этот параметр предназначен для использования в сценариях и командах, которые будут доставляться другим пользователям. Допустимыми значениями для этого параметра являются:

Публичный. Переменная видна. (По умолчанию используется значение Public.) Частное. Переменная не видна.

Когда переменная является частной, она не отображается в списках переменных, таких как те, которые возвращаются Get-Variable , или в отображениях переменной: drive . Команды для чтения или изменения значения частной переменной возвращают ошибку. Однако пользователь может запускать команды, использующие закрытую переменную, если команды были написаны в сеансе, в котором была определена переменная.

Или эта статья:

Свойства переменных PowerShell: описание, видимость, параметры, атрибуты

Переменная видимость

Свойство Visibility переменной PowerShell может иметь значения Public или Private . По умолчанию используется значение Public . Не путайте это свойство с частной областью видимости. Свойство видимости позволяет разработчику модуля PowerShell скрывать переменные. Если вы импортируете модуль с переменными, для видимости которых задано значение Private, вы не сможете просмотреть или изменить переменную на консоли, даже если она находится в глобальной области видимости. Если вы попытаетесь получить доступ к частной переменной, PowerShell выдаст сообщение об ошибке: невозможно получить доступ к переменной ‘$myPrivateVariable’, поскольку это частная переменная.

Конечно, точечный поиск / запуск таких вещей загружает данные в память вашего текущего сеанса.

Комментарии:

1. Да, справочная информация мне не ясна. По крайней мере, я не могу точно определить описанное поведение из справочной информации. Может быть, вы могли бы? Кроме того, что вы имеете в виду, говоря «точечный поиск / запуск таких вещей загружает данные в память вашего текущего сеанса».? В тот момент, когда вы импортируете модуль, он уже находится в памяти вашего текущего сеанса. Импорт модуля — это, по сути, точечный поиск. И разве вы не замечаете определенного разрыва между двумя утверждениями «доступно только в текущей области» и «невозможно просмотреть или изменить переменную на консоли, даже если она находится в глобальной области»?

2. Может быть, немного конкретнее: «Переменная доступна только в текущей области». -> Хорошо, какова тогда область видимости переменной $mytest в моем примере? И почему эта область не доступна напрямую с помощью консоли, но доступна с помощью scriptblock, который вызывается на консоли? Какова особая связь между областью действия этого scriptblock и областью действия $mytest, которая делает это возможным?

3. Официальная документация действительно отсутствует, @Hermjard. postanote, вы цитируете -Option параметр, который не связан с -Visibility параметром he. Вторая цитата (сторонний источник) на самом деле не объясняет эту функцию, учитывая, что переменные модуля по умолчанию скрыты от импортера. Как показано в вопросе, ограничение видимости тривиально обходится.

4. Исправлена ошибка -option, так что, да, приказал вырезать / вставить из документа. Что касается 3rdP, это еще один вариант источника. Итак, если то, что вы говорите здесь, заключается в том, что оба они недействительны (хотя у меня не было причин нуждаться в том, что пытается сделать OP), то на стороне MS Docs можно предположить, что нужно обратиться к команде MS PowerShell docs для исправления и / или предоставлениябольше ясности для всех нас.