about_Scopes

简短说明

说明 PowerShell 中的作用域的概念,并演示如何设置和更改元素的作用域。

长说明

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

下面是范围的基本规则:

  • 范围可以嵌套。 外部作用域称为父作用域。 任何嵌套的作用域都是该父级的子作用域。

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

  • 在范围内创建的项只能在创建它的范围内更改,除非显式指定了不同的范围。

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

PowerShell 范围

PowerShell 支持以下作用域:

  • 全局: PowerShell 启动或创建新会话或运行空间时生效的作用域。 在全局作用域(例如自动变量和首选项变量)中创建了 PowerShell 启动时出现的变量和函数。 PowerShell 配置文件中的变量、别名和函数也是在全局范围内创建的。 全局范围是会话中的根父范围。

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

  • 脚本:运行脚本文件时创建的作用域。 只有脚本中的命令在脚本作用域中运行。 对于脚本中的命令,脚本范围为本地范围。

备注

私有 不是范围。 它是一个 选项 ,它更改项的范围之外的项的可见性。

父范围和子范围

可以通过调用脚本或函数来创建新的子作用域。 调用范围为父范围。 被调用的脚本或函数是子作用域。 您调用的函数或脚本可以调用其他函数,从而创建子范围的层次结构,该层次结构的根作用域是全局作用域。

除非显式将项设置为私有,否则父范围中的项可用于子范围。 但是,在子范围内创建和更改的项不影响父范围,除非你在创建项时显式指定范围。

备注

模块中的函数不在调用作用域的子范围中运行。 模块具有其自己的会话状态,该状态链接到全局范围。 所有模块代码都在具有自身根作用域的特定于模块的范围层次结构中运行。

继承

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

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

若要查找特定范围中的项,请使用或的作用域参数 Get-Variable Get-Alias

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

Get-Variable -Scope local

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

Get-Variable -Scope global

作用域修饰符

变量、别名或函数名可以包含以下任意一种可选的作用域修饰符:

  • global: -指定名称存在于 全局 范围内。

  • local: -指定名称存在于 本地 作用域中。 当前范围始终是 本地 范围。

  • private: -指定该名称是 私有 的,仅对当前作用域可见。

  • script: -指定名称存在于 脚本 作用域中。 如果没有最近的上级脚本文件,则 脚本 范围是最近的上级脚本文件的范围或 全局

  • using: -用于在通过 cmdlet (如和)运行脚本时访问在其他作用域中定义的变量 Start-Job Invoke-Command

  • workflow: -指定名称存在于工作流中。 注意: PowerShell v6 和更高版本不支持工作流。

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

    命名空间 说明
    Alias: 当前作用域中定义的别名
    Env: 在当前作用域中定义的环境变量
    Function: 当前作用域中定义的函数
    Variable: 当前作用域中定义的变量

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

使用作用域修饰符

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

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

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

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

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

以下不使用范围修饰符的命令将在当前或 本地 范围内创建变量:

$a = "one"

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

$global:a = "one"

若要在 脚本 作用域中创建相同的变量,请使用 script: 作用域修饰符:

$script:a = "one"

还可以将作用域修饰符用于函数。 以下函数定义将在 全局 范围内创建函数:

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

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

$test
$global:test

Using:作用域修饰符

使用是一个特殊的作用域修饰符,用来标识远程命令中的局部变量。 如果不使用修饰符,则 PowerShell 应在远程会话中定义远程命令中的变量。

UsingPowerShell 3.0 中引入了作用域修饰符。

对于任何执行进程外的脚本或命令,都需要 Using 范围修饰符来嵌入调用会话范围内的变量值,以便使会话代码不能访问它们。 Using以下上下文支持作用域修饰符:

  • 远程执行的命令,已开始 Invoke-Command 使用 ComputerNameHostNameSSHConnectionSession 参数 (远程会话)
  • 后台作业, Start-Job (进程外会话开始)
  • 通过 Start-ThreadJobForEach-Object -Parallel (单独的线程会话启动的线程作业)

根据上下文,嵌入变量值是调用方的作用域中的数据的独立副本或对其的引用。 在远程和进程外会话中,它们始终是独立的副本。

有关详细信息,请参阅 about_Remote_Variables

在线程会话中,它们是通过引用传递的。 这意味着可以在不同的线程中修改调用范围变量。 安全修改变量需要线程同步。

有关详细信息,请参阅:

变量值的序列化

远程执行的命令和后台作业在进程外运行。 进程外会话使用基于 XML 的序列化和反序列化,使变量的值跨进程边界可用。 序列化过程将对象转换为包含原始对象属性,但不包含其方法的 PSObject。

对于有限的一组类型,反序列化将对象重新Hydrate back to original 类型。 rehydrated 对象是原始对象实例的副本。 它具有类型属性和方法。 对于简单类型(如 System.Version), 副本是确切的。 对于复杂类型,副本是不完整的。 例如,重新冻结的证书对象不包含私钥。

所有其他类型的实例都是 PSObject 实例。 PSTypeNames 属性包含以反初始化 为前缀的原始类型名称,例如 Deserialized.System.Data.DataTable

AllScope 选项

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

具有 AllScope 属性 的项在子作用域中可见,并且属于该作用域。 更改任何作用域中的项都会影响定义变量的所有作用域。

管理范围

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

Get-Help * -Parameter scope

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

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

Get-Variable -Scope local

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

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

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

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

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

备注

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

将点源表示法与范围一同使用

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

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

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

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

c:\scripts\sample.ps1

若要在本地Sample.ps1脚本,请使用以下命令:

. c:\scripts.sample.ps1

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

& c:\scripts.sample.ps1

可以在 中阅读有关调用运算符about_operators。

当前范围中Sample.ps1脚本创建的任何别名、函数或变量。

限制无范围

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

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

会话

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

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

模块

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

默认情况下,模块将加载到当前会话状态(而不是当前范围 )的 顶级 中。 当前会话状态可以是模块会话状态或全局会话状态。 将模块添加到会话不会更改范围。 如果你在全局范围内,则模块将加载到全局会话状态。 任何导出都放置在全局表中。 如果从 module1 中加载 module2,module2 将加载到 module1 的会话状态,而不是全局会话状态。 来自 module2 的任何导出都位于 module1 会话状态的顶部。 如果使用 Import-Module -Scope local ,则导出将放入当前范围对象而不是顶级。 如果你在模块中,并使用 (或) 加载另一个模块,该模块及其导出将加载到全局会话状态而不是模块的本地 Import-Module -Scope global Import-Module -Global 会话状态。 此功能旨在编写用于操作模块的模块。 WindowsCompatibility 模块会这样做,将代理模块导入全局会话状态。

在会话状态中,模块具有其自己的作用域。 请考虑以下模块 C:\temp\mod1.psm1

$a = "Hello"

function foo {
    "`$a = $a"
    "`$global:a = $global:a"
}

现在,我们将创建一个全局变量 $a ,为它提供一个值并调用 函数 foo

$a = "Goodbye"
foo

模块在模块范围中声明变量 $a ,然后函数 foo 在这两个作用域中输出变量的值。

$a = Hello
$global:a = Goodbye

嵌套提示

嵌套提示没有其自己的范围。 输入嵌套提示时,嵌套提示是环境的子集。 但是,你仍位于本地范围内。

脚本具有其自己的范围。 如果要调试脚本,并且到达脚本中的断点,则输入脚本范围。

专用选项

别名和变量具有 Option 属性,该属性的值为 Private。 可以在创建 项的 作用域中查看和更改具有"专用"选项的项,但不能在作用域外查看或更改这些项。

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

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

可见性

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

Visibility 属性采用 Public 和 Private 值。 只能在创建具有专用可见性的容器中查看和更改这些项。 如果添加或导入容器,则不能查看或更改具有专用可见性的项。

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

  • 如果创建的项在全局范围内具有私有可见性,则无法查看或更改任何范围中的项。
  • 如果尝试查看或更改具有私有可见性的变量的值,则 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"

接下来,创建一个定义变量的 Sample.ps1 脚本 $test 。 在脚本中,使用范围修饰符来引用变量的全局或局部版本 $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"

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

在 Sample.ps1 中:

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

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

PS>  $test
Local

示例4:创建私有变量

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

下面的命令创建一个 $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}

使用范围是在 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

Start-ThreadJob