Что именно означает Visibility Private?

Я пишу следующее в модуль сценария (т. е. файл 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}, я снова смогу получить доступ к своей приватной переменной — как же так? На всякий случай, если вы спросите, & {$mytest} работает так же, кстати.

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

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


person Hermjard    schedule 10.12.2020    source источник


Ответы (2)


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

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

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

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

  • Оснастки были предшественниками модуля только в двоичном виде из версии 1 и больше не актуальны.

  • Скрипты не имеют состояния за исключением случаев, когда они выполняются, поэтому идея доступа к его переменным извне обычно неприменима (см. ниже re dot-sources) .

  • Модули имеют состояние, но все переменные в модуле по умолчанию скрыты от внешнего мира, и нет необходимости задавать специальные свойства переменных. И наоборот, вы должны явно запросить экспорт переменной через Export-ModuleMember -Variable (а также через запись VariablesToExport в соответствующем манифесте модуля, если он присутствует).

    • Note: For functions, the logic is different, because - unlike variables - all functions are exported by default. To make functions effectively private, i.e. to hide them from consumers of your module, you must switch to explicit exporting: If your module has no manifest, use an Export-ModuleMember -Function ... call at the bottom of your script module to select the functions to export explicitly. If you do have a manifest (a satellite *.psd1 file), it is sufficient to list the functions of interest in the FunctionsToExport entry. Note that both Export-ModuleMember and the FunctionsToExport module-manifest entry accept name patterns (wildcards); thus, for instance, if you adopt a convention of using the verb-noun naming convention only for public functions in your module, a FunctionsToExport = @( '*-*' ) entry would automatically limit exporting to the functions implicitly designated as public.

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

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

  • Когда скрипт или функция запускается, они могут скрывать свои переменные от потомков областей действия с помощью Private option /scope; например.:

      $private:foo = 'bar' # create variable only visible in the very same scope
      "in same scope: [$foo]"        # -> 'in same scope: [bar]'
      & { "in child scope: [$foo]" } # -> 'in child scope: []'
    
  • Если сценарий предназначен для точечного поиска, то есть для загрузки непосредственно в текущую область с помощью . operator и вы хотите выборочно скрыть созданные в нем переменные, самое простое решение - заключить такие переменные в & { ... }, чтобы создать их в дочерней области, которая отбрасывается при выходе из блока скрипта; в качестве альтернативы, но менее надежно, вы также можете очистить явно через 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.

Как вы заметили, это ограничение легко обойти, обратившись к переменной через блок скрипта, как . { $mytest }, так и & { $mytest } — это работает и со скрытыми командами.

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

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

  • Я подозреваю, что мы имеем дело с ошибкой, которая не всегда обеспечивает невидимость.

  • Когда он применяется, любопытно, что невидимая переменная не просто обрабатывается как если бы она не существовала, а ошибка; напротив, невидимая команда действительно выглядит так, как будто ее не существует.[1]

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

    • Указывает, разрешено ли выполнение команды по запросу, внешнему по отношению к пространству выполнения.

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


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

person mklement0    schedule 10.12.2020
comment
Поэтому все потребности в видимости должны быть удовлетворены даже без свойства частной видимости - я должен признать, что изначально у меня было более сложное намерение, но я хотел начать с упрощенного вопроса, чтобы не было слишком много встречных вопросов. Моим первоначальным намерением было сделать функции модуля видимыми, иметь в модуле вспомогательные функции, которые могут обращаться произвольно друг к другу, но не видны снаружи. К сожалению, с scope private вы достигаете прямо противоположного: он блокирует доступ между функциями, но по-прежнему разрешает доступ импортеру. - person Hermjard; 12.12.2020
comment
На тот случай, если вам интересно, как я сделал функции закрытыми для видимости (потому что, к сожалению, нет командлета New-Function): после определения функции Test-Function, Я помещаю следующую команду в конец модуля: (Get-Command -Name 'Test-Function').Visibility = [System.Management.Automation.SessionStateEntryVisibility]::Private. - person Hermjard; 12.12.2020
comment
Я хочу избежать использования командлета Export-ModuleMember или FunctionsToExport соответственно, потому что вам придется адаптировать его каждый раз, когда вы добавляете (экспортируемую) новую функцию в модуль. Видимость private, согласно ее официальному описанию, должна идеально подходить для моей цели (и, возможно, даже для причины ее существования), но я обнаружил, что она работает не так, как ожидалось. И поэтому я хотел знать, почему. - person Hermjard; 12.12.2020
comment
Функции, которые не экспортируются, не являются частными. Их просто не экспортируют. Это разница. И это имеет значение, если вы попытаетесь решить ту же проблему при импорте файла ps1 вместо файла psm1. - person Hermjard; 13.12.2020
comment
Любопытно, что когда он применяется, невидимая переменная не просто обрабатывается так, как если бы она не существовала, а вместо этого сообщается об ошибке. - Интересно, что приватная функция, к которой вы безуспешно пытаетесь получить доступ, обрабатывается так, как если бы она не существовала. - person Hermjard; 13.12.2020
comment
@ mkelement0, ваш ответ, кажется, предполагает, что необходимо использовать Get-Command для изменения видимости функции. Но необходимо только, чтобы получить объект функции, чтобы изменить его свойство видимости. Как вы получите этот объект, совершенно не имеет значения. Кстати, свойство Visibility доступно не только для чтения после импорта модуля. Это означает, что вы даже можете сделать функцию задним числом частной из оболочки. Кстати, как только вы сделали это приватным, я не знаю, как это отменить. - person Hermjard; 13.12.2020
comment
Ок, только что нашел способ откатить невидимку: (Get-Module MyModule).ExportedFunctions['Test-Function'].Visibility = 'Public'. Вы можете сделать это, даже если функция была сделана невидимой уже внутри самого модуля. Вы видите, что приватная функция не исчезает из списка экспортируемых функций (если экспортируется). Намек на то, что Экспорт и Видимость — это разные понятия. Или были бы разные концепции, если бы последняя концепция работала по назначению. - person Hermjard; 13.12.2020
comment
Для меня это становится сложным... пространства выполнения и контейнеры - это одно и то же? Или вы хотите сказать, что видимость-приватность для переменных и видимость-приватность для функций различны по своей природе? И если вы говорите, что видимость-приватность не предназначена для использования внутри сеанса, то почему тогда она частично эффективна при использовании внутри сеанса? - person Hermjard; 13.12.2020
comment
@Hermjard: Очевидно, то, что вы называете частичной эффективностью, представляет собой очень бессистемный, непоследовательный, слишком легко обходимый способ. Опять же: учитывая непоследовательное поведение с ошибками, нехватку документации, намекающей на использование между средами выполнения, и, что не менее важно, видимость/конфиденциальность внутри среды выполнения лучше управляются другими, лучше документированными и не содержащими ошибок функциями, я вижу бесполезно заниматься этим для ваших целей. Внесение поправок в документы заслуживает внимания, но я не ожидаю, что это будет актуально для большинства пользователей. Нет, пространства выполнения и контейнеры — это не одно и то же. - person mklement0; 13.12.2020
comment
@Hermjard: пространство выполнения — это отдельный поток, который запускает PowerShell, например интерактивный сеанс. В интерактивном сеансе несколько пространств выполнения вступают в игру только неявно, ситуативно, например, через фоновые/потоковые задания или удаленное взаимодействие, или в версии 7+. через ForEach-Object -Parallel. С помощью PowerShell SDK возможно явное создание пространства выполнения. - person mklement0; 13.12.2020

Вы говорите, что справочная информация во встроенном файле справки New-Variable не ясна:

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

-Видимость

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

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

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

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

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

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

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

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

person postanote    schedule 10.12.2020
comment
Да, справочная информация мне непонятна. По крайней мере, я не могу точно вывести описанное поведение из справочной информации. Может быть, вы могли бы? Кроме того, что вы имеете в виду под точечным поиском/запуском таких вещей, которые загружают материал в память вашего текущего сеанса? В тот момент, когда вы импортируете модуль, он находится уже в памяти вашего текущего сеанса. Импорт модуля является в основном точечным поиском. И разве вы не замечаете определенный разрыв между двумя операторами, доступными только в текущей области видимости, и не можете просмотреть или изменить переменную в консоли, даже если она находится в глобальной области видимости? - person Hermjard; 11.12.2020
comment
Может быть, немного более конкретно: переменная доступна только в текущей области. -› Хорошо, тогда какова область действия переменной $mytest в моем примере? И почему эта область недоступна напрямую из консоли, но доступна через блок сценария, который вызывается на консоли? Какова особая связь между областью действия этого блока сценария и областью видимости $mytest, которая делает это возможным? - person Hermjard; 11.12.2020
comment
Официальной документации действительно не хватает, @Hermjard. postanote, вы цитируете параметр -Option, который не имеет отношения к параметру -Visibility. Вторая цитата (сторонний источник) на самом деле не объясняет эту функцию, учитывая, что переменные модуля скрыты от импортера по умолчанию. Как показано в вопросе, ограничение видимости легко обходится. - person mklement0; 11.12.2020
comment
Исправлена ​​опция -option, так что, да, можно было вырезать/вставить из документа. Что касается 3rdP, то это другой исходный вариант. Итак, если то, что вы здесь говорите, заключается в том, что оба они недействительны (хотя у меня не было причин нуждаться в том, что пытается сделать ОП), то со стороны MS Docs можно было бы предположить, что нужно поднять дело с группе документации MS PowerShell, чтобы исправить и/или внести больше ясности для всех нас. - person postanote; 11.12.2020