об областях в Powershell

Aug 24 2020

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

  1. О «Локальной области»: из того, что я читал, локальная область всегда является текущей областью. Итак, по умолчанию, когда мы создаем элемент (без модификатора области видимости), например, переменную, в некоторой области, пусть это будет сценарий или глобальная область, соответственно область действия будет script / global. Итак, мой вопрос: когда нам нужно будет явно указать localмодификатор?
  2. MSDN говорит:

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

Но когда я пробую следующее:

PS> $Name = "John" PS> Powershell.exe PS>echo $Name  // No Output

Из приведенной выше цитаты кажется, что «запуск нового экземпляра powershell» является дочерней областью, поэтому все элементы в родительской области должны быть видны там. Кто-нибудь может объяснить?

Ответы

2 mklement0 Aug 24 2020 at 23:16

В дополнение к полезному ответу iRon :

  1. [...] когда нам нужно будет явно указать локальный модификатор?

$local:редко требуется , потому что локальная область видимости подразумевается при отсутствии спецификатора области.

Однако это применимо только в том случае, если указанная переменная действительно существует как локальная переменная , учитывая, что динамическая область видимости PowerShell делает переменные из родительских (родительских) областей также видимыми для дочерних (дочерних) областей (см. Этот ответ для получения дополнительной информации):

  • Например, предположим, что вы $foo = 'bar'объявили в глобальной области видимости, а затем ссылка на $fooв сценарии будет искать сначала локальный $foo экземпляр; если его нет, используется $fooопределенная в наследственной (родительской) области видимости, если таковая имеется, которая в этом примере будет глобальной $foo и 'bar'будет возвращена.

  • Напротив, если в вашем скрипте вы используете $local:fooбез определения локальной $fooпеременной, вы либо получаете $nullпо умолчанию, либо, если Set-StrictMode -Version 2действует или выше, возникает ошибка, завершающая оператор .


  1. MSDN сообщает: [...] путем создания сеанса или запуска нового экземпляра PowerShell [...] результатом является родительская область (исходная область) и дочерняя область (область, которую вы создали).

Документация является неверным в связи с этим , как это письмом (а вопрос GitHub был подан):

  • Родственные (родительско-дочерние) отношения между областями действия существуют только в контексте данного сеанса (области выполнения) .

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

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

  • Поэтому не ребенок сферы вызывающей рамки не создается в следующих сценариях, где недавно запущенный код ничего не знает о переменных (и других определениях) в вызывающей сфере :

    • Запуск нового сеанса через удаленное взаимодействие PowerShell (например, с помощью Enter-PSSession) илиInvoke-Command -Computer

    • Запуск фонового задания [поток] с Start-Jobили Start-ThreadJobили запуск потоков параллельно с ForEach-Object -Parallelв v7.0 +

    • Запуск нового экземпляра PowerShell (процесс) , с помощью PowerShell CLI ( pwshдля PowerShell [Core], powershell.exeдля Windows PowerShell).

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

      • При вызове интерфейса командной строки или использовании Start-Job, когда дочерний процесс создается на том же компьютере, только переменные среды, определенные в вызывающем процессе, становятся автоматически доступными для дочернего процесса.
      • В противном случае значения от вызывающего должны передаваться как аргументы или - за исключением случаев использования интерфейса командной строки - через $using:область видимости - см. Этот ответ .
4 iRon Aug 24 2020 at 14:48

Начиная с последнего вопроса:

Области действия вступают в игру с функциями и вызываемыми скриптами (командлетами), например:

Function Test {
    $Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test

Возврат:

Local: 6
Global: 5

А также:

Function Test {
    $Global:Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test

Возврат:

Local: 6
Global: 6

Или если вы поместите функцию в скрипт (например MyScript.ps1):

$Test = 5 .\MyScript.ps1 Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script

Что вернет в основном те же результаты, что и выше, если вы не укажете источник вашего скрипта, где он будет запускаться в текущей области:

$Test = 5 . .\MyScript.ps1 Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test

Для того, что вы делаете:
вы создаете полностью новый сеанс PowerShell (с Powershell.exe), который начнется со свежего списка переменных.
Обратите внимание, что вы снова увидите исходные переменные, если вы exitиз нового сеанса:

PS C:\> $Name = "John" PS C:\> Powershell.exe Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. Try the new cross-platform PowerShell https://aka.ms/pscore6 PS C:\> Write-Host 'New session' $Name
New session
PS C:\> Exit
PS C:\> Write-Host 'Initial session' $Name
Initial session John

Что касается первого вопроса, я не думаю, что есть много приложений, где вам нужно явно ссылаться на $Localобласть видимости, но чтобы дать вам пример, где вы могли бы ее использовать:

$Test = 5 Function Test { Write-Host ($Local:Test++)
}
Test

В приведенном выше примере унарный оператор инкремента будет начинаться с, 0если вы явно используете $Localобласть видимости (на самом деле вы начинаете с пустой локальной переменной, к которой будет приведено приведение 0) и с, 5если вы опустите $Localобласть, в которой вы унаследуете копию $Testпеременной из родительская область.