范围如何影响PowerShell脚本

PS-Scope_Final-001

在批处理脚本中,环境变量的更改默认对当前会话有全局影响。 对于PowerShell,恰恰相反是真的,因为范围用于隔离脚本的修改。 在这里,我们将探讨范围如何影响PowerShell脚本以及如何在其中和周围工作。

什么是范围?

在PowerShell中,“范围”是指脚本或命令shell正在其中操作的当前环境。 范围用于保护环境中的某些对象免于被脚本或函数无意地修改。 特别地,除非这些命令中的参数另有规定,否则以下内容不受从另一个范围运行的命令的修改:

  • 变量
  • 别名
  • 功能
  • PowerShell驱动器(PSDrives)

只要运行脚本或函数,或创建新的会话或PowerShell实例时,都会创建新作用域。 通过运行脚本和函数创建的作用域与创建它们的作用域具有“父/子”关系。 有几个范围有特别的意义,可以通过名称访问:

  • 全局作用域是PowerShell启动时创建的作用域。 它包括PowerShell内置的变量,别名,函数和PSDrives以及由PowerShell配置文件创建的任何变量,别名,函数和PSDrive。
  • 局部范围是指当前范围的任何内容。 当您启动PowerShell时,它将引用全局范围,在脚本中它将是脚本范围等。
  • 脚本范围是在运行脚本时创建的。 在此范围内操作的唯一命令是脚本中的那些命令。
  • 可以在当前范围内定义私有作用域,以防止其他作用域中的命令能够读取或修改他们原本可以访问的项目。

范围也可以通过某些命令中的数字引用,其中当前范围称为零,其祖先由增加的整数引用。 例如,在从全局范围运行的脚本中,脚本作用域将为0,全局作用域将为1.进一步嵌套在脚本作用域内的作用域(例如函数)将全局作用域称为2 。负数不会用于引用子作用域 - 原因很快就会显现。

范围如何影响命令

如前所述,在一个作用域内执行的命令不会影响另一个作用域中的东西,除非明确告知这样做。 例如,如果$ MyVar存在于全局范围内,并且脚本运行命令将$ MyVar设置为不同的值,则全局版本的$ MyVar将保持不变,而$ MyVar的副本将放置在脚本范围中,值。 如果$ MyVar不存在,脚本将默认在脚本范围内创建它,而不是在全局范围中。 当你了解范围之间的实际父/子关系时,这一点很重要。

PowerShell中范围的父/子关系是单向的。 命令可以查看并可选修改当前作用域及其父作用域以及其上的任何作用域。 但是,它们无法查看或修改当前作用域的任何子项中的内容。 这主要是因为,一旦迁移到父作用域,子作用域已经被销毁,因为它已经达到其目的。 例如,为什么在脚本终止后,您必须从脚本范围中的全局范围中查看或修改变量? 有很多情况下,你需要一个脚本或函数的更改以保持超出其完成,但不是那么多,你需要在脚本或函数的范围内更改对象之前或之后运行。 (通常,这样的东西将作为脚本或函数本身的一部分来处理。)

当然,什么是规则没有例外? 上面的一个例外是私有范围。 私有作用域中的对象只能在创建它们的作用域中运行的命令访问。 另一个重要的例外是具有AllScope属性的项。 这些是特殊变量和别名,任何范围内的更改将影响所有范围。 以下命令将显示哪些变量和别名具有AllScope属性:

Get-Variable | Where-Object {$_.Options -match AllScope}
Get-Alias | Where-Object {$_.Options -match AllScope)

行动范围

为了我们首先看看动作范围,我们将在PowerShell会话中开始,其中变量$ MyVar已经从命令行设置为字符串“我是全局变量!”。 然后,将从名为Scope-Demo.ps1的文件运行以下脚本:

Function FunctionScope
{
    Changing $MyVar with a function.
    $MyVar = I got set by a function!
    "MyVar says $MyVar"
}

Checking current value of $MyVar.
"MyVar says $MyVar"

Changing $MyVar by script.
$MyVar = I got set by a script!
"MyVar says $MyVar"

FunctionScope

Checking final value of MyVar before script exit.
"MyVar says $MyVar"

如果PowerShell脚本与批处理脚本一样工作,我们期望$ MyVar(或批处理语法中的%MyVar%)的值从“我是全局变量!”更改为“我已由脚本设置! ,最后到“我被一个函数设置了! 它将保持直到它被显式地改变或会话终止。 但是,当我们遍历每个范围时,特别是在FunctionScope函数完成其工作后,我们再次从脚本中检查变量,然后再次检查全局范围。

image

正如你可以看到,当我们移动通过脚本,变量似乎改变,因为,直到FunctionScope函数完成,我们检查的变量从同一范围内它最后更改。 在完成FunctionScope之后,我们回到了脚本范围,其中$ MyVar没有被函数触及。 然后,当脚本终止时,我们回到全局范围,在那里它根本没有被修改。

到达局部范围之外

所以,这是所有好,很好,帮助你防止意外地应用环境变化超出你的脚本和函数,但如果你真的想做这样的修改怎么办? 有一个特殊的,相当简单的语法用于创建和修改本地作用域之外的对象。 您只需将范围名称放在变量名称的开头,并在范围和变量名称之间放置冒号。 喜欢这个:

$global:MyVar
$script:MyVar
$local:MyVar

在查看和设置变量时,您可以使用这些修饰符。 让我们看看这个演示脚本会发生什么:

Function FunctionScope
{
    
    Changing $MyVar in the local function scope...
    $local:MyVar = "This is MyVar in the functions local scope."
    Changing $MyVar in the script scope...
    $script:MyVar = MyVar used to be set by a script. Now set by a function.
    Changing $MyVar in the global scope...
    $global:MyVar = MyVar was set in the global scope. Now set by a function.
    
    Checking $MyVar in each scope...
    "Local: $local:MyVar"
    "Script: $script:MyVar"
    "Global: $global:MyVar"
    
}

Getting current value of $MyVar.
"MyVar says $MyVar"

Changing $MyVar by script.
$MyVar = I got set by a script!
"MyVar says $MyVar"

FunctionScope

Checking $MyVar from script scope before exit.
"MyVar says $MyVar"

与前面一样,我们将首先在全局范围中设置变量,然后检查最终的全局范围结果。

image

在这里,您可以看到FunctionScope能够更改脚本作用域中的变量,并在更改完成后保留更改。 此外,即使在脚本退出后,对全局范围中的变量的更改仍然保持。 如果您必须使用相同的代码在脚本中或在全局范围内重复更改变量,那么这可能特别有用 - 您只需定义一个函数或脚本,以便修改变量在何处以及如何完成,以及当需要这些更改时请求。

如前所述,范围编号也可以在某些命令中用于修改与局部范围相关的不同级别的变量。 下面是上面第二个示例中使用的相同脚本,但是修改了函数以使用带有作用域编号的Get-Variable和Set-Variable命令,而不是直接引用具有命名作用域的变量:

Function FunctionScope
{
    
    Changing $MyVar in scope 0, relative to FunctionScope...
    Set-Variable MyVar "This is MyVar in the functions scope 0." –Scope 0
    Changing $MyVar in scope 1, relative to FunctionScope...
    Set-Variable MyVar MyVar was changed in scope 1, from a function. –Scope 1
    Changing $MyVar in scope 2, relative to Functionscope...
    Set-Variable MyVar MyVar was changed in scope 2, from a function. –Scope 2
    
    Checking $MyVar in each scope...
    ‘Scope 0:’
    Get-Variable MyVar –Scope 0 –ValueOnly
    ‘Scope 1:’
    Get-Variable MyVar –Scope 1 –ValueOnly
    ‘Scope 2:’
    Get-Variable MyVar –Scope 2 –ValueOnly
    
}

Getting current value of $MyVar.
"MyVar says $MyVar"

Changing $MyVar by script.
$MyVar = I got set by a script!
"MyVar says $MyVar"

FunctionScope

Checking $MyVar from script scope before exit.
"MyVar says $MyVar"
Get-Help about_scopes

与之前类似,我们可以看到一个作用域中的命令如何修改其父作用域中的对象。

附加信息

还有更多的可以做的范围比可以适合这篇文章。 范围不仅仅影响变量,还有更多要了解私有范围和AllScope变量。 有关更多有用的信息,可以在PowerShell中运行以下命令:

 获取帮助about_scopes 

同样的帮助文件也可在TechNet上获得


Scope image credit: spadassin on openclipart

赞 (0)
分享到:更多 ()