关于 Scopes

简短说明

介绍 PowerShell 中的作用域概念,并演示如何设置和更改元素的范围。

详细说明

PowerShell 通过限制可以读取和更改的地方,来保护对变量、别名、函数和 PowerShell 驱动器 (PSDrives) 的访问。 PowerShell 使用范围规则来确保不会无意中更改不应更改的项。

以下是范围的基本规则:

  • 作用域可以嵌套。 外部作用域被称为父作用域。 所有嵌套的作用域都是该父级的子作用域。

  • 项在创建它的作用域和任何子作用域中都可见,除非您明确将其设为私有。 您可以将变量、别名、函数或 PowerShell 驱动器放置在一个或多个范围内。

  • 您在某个范围内创建的项只能在创建它的该范围内更改,除非您明确指定了其他范围。

如果在范围内创建项,并且该项与其他范围内的项共享其名称,则原始项可能会隐藏在新项下,但不会被覆盖或更改。

PowerShell 范围

PowerShell 支持以下范围:

  • Global:PowerShell 启动时生效的范围。 PowerShell 启动时存在的变量和函数是在全局范围内创建的,例如自动变量和首选项变量。 PowerShell 配置文件中的变量、别名和函数也在全局范围内创建。

  • Local:当前范围。 本地范围可以是全局范围或任何其他范围。

  • Script(脚本):在脚本文件运行时创建的范围。 只有脚本中的命令在脚本范围内运行。 对于脚本中的命令,脚本范围是本地范围。

注释

Private 不是一个范围。 这是一个 选项 ,用于更改定义项的范围之外的项的可见性。

父范围和子范围

您可以通过运行脚本或函数、创建会话或启动 PowerShell 的新实例来创建新范围。 创建新范围时,结果是父范围 (原始范围) 和子范围 (您创建的范围)。

在 PowerShell 中,所有范围都是全局范围的子范围,但你可以创建多个范围和多个递归范围。

除非显式地将项设为私有,否则父作用域中的项可用于子作用域。 但是,您在子作用域中创建和更改的项不会影响父作用域,除非您在创建项时显式指定作用域。

继承

子范围不会从父范围继承变量、别名和函数。 除非项是私有的,否则子范围可以查看父范围中的项。 而且,它可以通过显式指定父范围来更改项,但这些项不是子范围的一部分。

但是,子范围是使用一组项创建的。 通常,它包括具有 AllScope 选项的所有别名。 本文稍后将讨论此选项。 它包括具有 AllScope 选项的所有变量,以及一些自动变量。

若要查找特定作用域中的项,请使用 Get-VariableGet-Alias的 Scope 参数。

例如,若要获取本地范围中的所有变量,请键入:

Get-Variable -Scope local

若要获取全局范围中的所有变量,请键入:

Get-Variable -Scope global

范围修饰符

变量、别名或函数名称可以包括以下任一可选作用域修饰符:

  • global: - 说明名称存在于全局范围中。

  • local: - 说明名称存在于本地范围中。 当前范围始终是 Local 范围。

  • private: - 说明名称是私有的,并且仅对当前范围可见。

  • script: - 说明名称存在于 脚本 范围内。 脚本范围是最近的上级脚本文件的范围或全局范围(如果没有最近的上级脚本文件)。

  • using: - 用于在通过 cmdlet 运行脚本时访问在另一个范围中定义的变量,例如 Start-JobInvoke-Command

  • workflow: - 指定工作流中存在该名称。 注意:PowerShell Core 不支持工作流。

  • <variable-namespace> - 由 PowerShell PSDrive 提供程序创建的修饰符。 例如:

    Namespace DESCRIPTION
    Alias: 当前作用域内定义的别名
    Env: 在当前作用域中定义的环境变量
    Function: 当前范围中定义的函数
    Variable: 在当前范围内定义的变量

脚本的默认作用域是脚本范围。 函数和别名的默认范围是本地范围,即使它们是在脚本中定义的。

使用作用域修饰符

若要指定新变量、别名或函数的范围,请使用范围修饰符。

变量中作用域修饰符的语法为:

$[<scope-modifier>:]<name> = <value>

函数中作用域修饰符的语法为:

function [<scope-modifier>:]<name> {<function-body>}

以下命令(不使用 scope 修饰符)在当前或 局部 范围内创建变量:

$a = "one"

若要在 全局 范围中创建相同的变量,请使用范围 global: 修饰符:

$global:a = "one"

若要在 脚本 范围内创建相同的变量,请使用 script: 范围修饰符:

$script:a = "one"

还可以将作用域修饰符与函数一起使用。 以下函数定义在全局作用域内创建函数:

function global:Hello {
  Write-Host "Hello, World"
}

还可以使用范围修饰符来引用不同作用域中的变量。 以下命令引用 $test 变量,首先位于本地范围,然后在全局范围内:

$test
$global:test

Using: 范围修饰符

“Using” 是一个特殊的作用域修饰符,用于在远程命令中标识局部变量。 如果没有修饰符,PowerShell 要求远程命令中的变量在远程会话中定义。

PowerShell 3.0 中引入了 Using scope 修饰符。

有关详细信息,请参阅 about_Remote_Variables

AllScope 选项

变量和别名具有 Option 属性,该属性可以采用 AllScope的值。 具有 AllScope 属性的项将成为您创建的任何子范围的一部分,尽管它们不会由父范围追溯继承。

具有 AllScope 属性的项在子范围中可见,并且它是该范围的一部分。 对任何作用域中的项所作的更改,都会影响变量定义所在的所有作用域。

管理范围

多个 cmdlet 具有 Scope 参数,可用于获取或设置特定作用域中的项(创建和更改)。 使用以下命令查找会话中具有 Scope 参数的所有 cmdlet:

Get-Help * -Parameter scope

若要查找在特定范围内可见的变量,请使用 ScopeGet-Variable 参数。 可见变量包括全局变量、父作用域中的变量和当前作用域中的变量。

例如,以下命令获取在本地范围内可见的变量:

Get-Variable -Scope local

若要在特定作用域中创建变量,请使用作用域修饰符或 Set-Variable参数。 以下命令在全局范围内创建变量:

New-Variable -Scope global -Name a -Value "One"

还可以使用 New-AliasSet-AliasGet-Alias cmdlet 的 Scope 参数来指定范围。 以下命令在全局范围内创建别名:

New-Alias -Scope global -Name np -Value Notepad.exe

若要获取特定作用域中的函数,请在该作用域内使用 Get-Item cmdlet。 cmdlet Get-Item 没有 Scope 参数。

注释

对于使用 Scope 参数的 cmdlet,您还可以通过编号来引用范围。 该数字描述一个范围到另一个范围的相对位置。 范围 0 表示当前或本地范围。 Scope 1 表示直接的父作用域。 Scope 2 表示父作用域的父级,以此类推。 如果已创建多个递归范围,则编号范围非常有用。

将 Dot Source Notation 与 Scope 一起使用

脚本和函数遵循范围的所有规则。 在特定的作用域中创建它们,它们仅影响该范围,除非使用 cmdlet 参数或范围修饰符来更改该范围。

但是,您可以使用点源表示法将脚本或函数添加到当前范围。 然后,当脚本在当前范围内运行时,该脚本创建的任何函数、别名和变量在当前范围内都可用。

要将函数添加到当前范围,请在函数调用中函数的路径和名称前键入点 (.) 和空格。

例如,要从脚本范围(脚本的默认值)中的 C:\Scripts 目录运行 Sample.ps1 脚本,请使用以下命令:

c:\scripts\sample.ps1

要在本地范围内运行 Sample.ps1 脚本,请使用以下命令:

. c:\scripts.sample.ps1

当您使用调用运算符 (&) 运行函数或脚本时,它不会添加到当前范围。 以下示例使用 call 运算符:

& c:\scripts.sample.ps1

您可以在 about_operators 中阅读有关呼叫运算符的更多信息。

Sample.ps1 脚本创建的任何别名、函数或变量在当前范围内不可用。

无范围限制

一些 PowerShell 概念类似于范围或与范围交互。 这些概念可能会与 scope 或 scope 的行为相混淆。

会话、模块和嵌套提示是自包含环境,但它们不是会话中全局作用域的子作用域。

会议

会话是运行 PowerShell 的环境。 在远程计算机上创建会话时,PowerShell 会建立与远程计算机的持久连接。 持久连接允许在会话中使用多个相关命令。

由于会话是包含的环境,因此它有自己的范围,但会话不是在其中创建它的会话的子范围。 会话从自己的全局作用域开始。 此范围独立于会话的全局范围。 可以在会话中创建子作用域。 例如,可以运行脚本在会话中创建子范围。

模块

可以使用 PowerShell 模块来共享和交付 PowerShell 工具。 模块是可以包含 cmdlet、脚本、函数、变量、别名和其他有用项的单元。 除非明确定义,否则模块中的项不能在模块外部访问。 因此,可以将模块添加到会话并使用公共项,而无需担心其他项可能会覆盖会话中的 cmdlet、脚本、函数和其他项。

模块的隐私行为类似于范围,但将模块添加到会话不会更改范围。 而且,该模块没有自己的范围,尽管模块中的脚本与所有 PowerShell 脚本一样,确实有自己的范围。

默认情况下,模块被加载到当前 会话状态 的顶层,而不是当前 范围。 这可以是模块会话状态或全局会话状态。 如果你在全局范围内,那么模块将被加载到全局会话状态中。 任何导出都将放入全局表中。 如果从 module1 加载 module2,则 module2 将加载到 module1 的会话状态中,而不是全局会话状态中。 从 module2 导出的任何数据都位于 module1 的会话状态的顶部。 如果使用 Import-Module -Scope local,那么导出将放入当前作用域对象而不是顶层。 如果你 在一个模块中 并使用 Import-Module -Scope global (or Import-Module -Global) 加载另一个模块,则该模块及其导出将加载到全局会话状态中,而不是模块的本地会话状态中。 此功能专为编写作模块的模块而设计。 WindowsCompatibility 模块执行此作是为了将代理模块导入到全局范围内。

嵌套提示

同样,嵌套提示也没有自己的范围。 输入嵌套提示时,嵌套提示是环境的子集。 但是,你仍留在本地作用域内。

脚本具有自己的作用域。 调试脚本时,如果到达了脚本的断点,则进入了脚本作用域。

私人选项

别名和变量具有 Option 属性,该属性的值为 Private。 具有 Private (私有 ) 选项的项目可以在创建它们的范围内查看和更改,但不能在该范围之外查看或更改它们。

例如,如果创建一个在全局范围内具有 private 选项的变量,然后运行脚本, Get-Variable 则脚本中的命令不会显示该 private 变量。 在此实例中使用 global scope 修饰符不会显示 private 变量。

可以使用 、、 Set-VariableNew-AliasSet-Alias cmdlet 的 New-VariableOption 参数将 Option 属性的值设置为 Private。

能见度

变量或别名的 可见性 属性决定了您是否可以在创建该项的容器外部看到该项。 容器可以是模块、脚本或管理单元。 Visibility 是为容器设计的,其方式与 Option 属性的 Private 值是为范围设计的相同。

Visibility 属性采用 PublicPrivate 值。 具有私有可见性的项只能在创建其的容器中查看和更改。 如果添加或导入容器,则无法查看或更改具有私有可见性的项目。

由于可见性是为容器设计的,因此它在作用域中的工作方式有所不同。

  • 如果创建的项在全局范围内具有私有可见性,则无法在任何范围内查看或更改该项。
  • 如果尝试查看或更改具有专用可见性的变量的值,PowerShell 将返回一条错误消息。

可以使用 New-VariableSet-Variable cmdlet 创建具有专用可见性的变量。

例子

示例 1:仅在脚本中更改变量值

以下命令更改脚本中 $ConfirmPreference 变量的值。 此更改不会影响全局范围。

首先,若要在本地范围内显示 $ConfirmPreference 变量的值,请使用以下命令:

PS>  $ConfirmPreference
High

创建包含以下命令的 Scope.ps1 脚本:

$ConfirmPreference = "Low"
"The value of `$ConfirmPreference is $ConfirmPreference."

运行脚本。 该脚本更改 $ConfirmPreference 变量的值,然后在脚本作用域中报告其值。 输出应类似于以下输出:

The value of $ConfirmPreference is Low.

接下来,测试当前范围内 $ConfirmPreference 变量的当前值。

PS>  $ConfirmPreference
High

此示例显示,对脚本作用域中变量值的更改不会影响该变量在父作用域中的值。

示例 2:查看不同范围内的变量值

可以使用范围修饰符查看本地范围和父范围中变量的值。

首先,在全局范围内定义 $test 变量。

$test = "Global"

接下来,创建一个定义 $test 变量的 Sample.ps1 脚本。 在脚本中,使用作用域修饰符引用 $test 变量的全局版本或本地版本。

在 Sample.ps1 中:

$test = "Local"
"The local value of `$test is $test."
"The global value of `$test is $global:test."

运行 Sample.ps1时,输出应类似于以下输出:

The local value of $test is Local.
The global value of $test is Global.

脚本完成后,会话中仅定义 $test 的全局值。

PS>  $test
Global

示例 3:更改父作用域中变量的值

除非使用 Private 选项或其他方法保护项目,否则可以在父范围中查看和更改变量的值。

首先,在全局范围内定义 $test 变量。

$test = "Global"

接下来,创建一个定义 $test 变量的 Sample.ps1 脚本。 在脚本中,使用作用域修饰符引用 $test 变量的全局版本或本地版本。

在 Sample.ps1 中:

$global:test = "Local"
"The global value of `$test is $global:test."

脚本完成后,将更改 $test 的全局值。

PS>  $test
Local

示例 4:创建私有变量

私有变量是具有值为 PrivateOption 属性的变量。 私有 变量由子作用域继承,但只能在创建它们的作用域中查看或更改它们。

以下命令创建一个在 local 范围内调用 $ptest 的私有变量。

New-Variable -Name ptest -Value 1 -Option private

您可以在本地范围内显示和更改 的值 $ptest

PS>  $ptest
1

PS>  $ptest = 2
PS>  $ptest
2

接下来,创建一个包含以下命令的 Sample.ps1 脚本。 该命令尝试显示和更改 的值 $ptest

在 Sample.ps1 中:

"The value of `$Ptest is $Ptest."
"The value of `$Ptest is $global:Ptest."

变量 $ptest 在脚本范围内不可见,输出为空。

"The value of $Ptest is ."
"The value of $Ptest is ."

示例 5:在远程命令中使用局部变量

对于在本地会话中创建的远程命令中的变量,请使用 Using 范围修饰符。 PowerShell 假定远程命令中的变量是在远程会话中创建的。

语法为:

$Using:<VariableName>

例如,以下命令在本地会话中创建 $Cred 变量,然后在远程命令中使用 $Cred 变量:

$Cred = Get-Credential
Invoke-Command $s {Remove-Item .\Test*.ps1 -Credential $Using:Cred}

Using 范围是在 PowerShell 3.0 中引入的。 在 PowerShell 2.0 中,要指示变量已在本地会话中创建,请使用以下命令格式。

$Cred = Get-Credential
Invoke-Command $s {
  param($c)
  Remove-Item .\Test*.ps1 -Credential $c
} -ArgumentList $Cred

另请参阅

about_Variables

about_Environment_Variables

about_Functions

about_Script_Blocks