作用域如何影响PowerShell脚本

在批处理脚本中,默认情况下,对环境变量的更改会对当前会话产生全局影响。对于PowerShell,情况正好相反,因为作用域用于隔离脚本的修改。在这里,我们将探索作用域如何影响PowerShell脚本,以及如何使用和绕过它们。

什么是范围?

在PowerShell中,“作用域”是指脚本或命令shell运行的当前环境。作用域用于保护环境中的某些对象不会被脚本或函数意外修改。具体地说,除非这些命令中的参数另有指定,否则以下内容不会被从其他作用域运行的命令修改:

变数。 别名。 功能。 PowerShell驱动器(PSD驱动器)

每当您运行脚本或函数,或者创建PowerShell的新会话或实例时,都会创建新的作用域。通过运行脚本和函数创建的作用域与从中创建它们的作用域具有“父/子”关系。有几个作用域具有特殊的含义,可以通过名称进行访问:

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

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

作用域如何影响命令

如前所述,在一个作用域中执行的命令不会影响另一个作用域中的内容,除非特别指示这样做。例如,如果全局作用域中存在$MyVar,并且脚本运行命令将$MyVar设置为不同的值,则$MyVar的全局版本将保持不变,而$MyVar的副本将使用新值放置在脚本作用域中。如果$MyVar不存在,默认情况下,脚本将在脚本范围内创建它,而不是在全局范围内。当您了解作用域之间的实际父/子关系时,记住这一点很重要。

PowerShell中作用域的父/子关系是单向的。命令可以查看当前作用域、其父作用域以及该父作用域之上的任何作用域,并可以选择修改这些作用域。但是,他们不能查看或修改当前范围的任何子级中的内容。这主要是因为,一旦您移动到父作用域,子作用域就已经被销毁,因为它已经实现了它的目的。例如,为什么必须在脚本终止后从全局范围查看或修改脚本范围内的变量?在很多情况下,您需要脚本或函数的更改在其完成后保持不变,但在运行脚本或函数之前或之后需要对脚本或函数作用域内的对象进行更改的情况并不多见。(通常,这类事情无论如何都会作为脚本或函数本身的一部分来处理。)

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

操作中的作用域

为了第一次了解作用域的作用,我们将从PowerShell会话开始,在该会话中,变量$MyVar已从命令行设置为字符串“我是一个全局变量!”然后,将从名为Scope-Demo.ps1的文件运行以下脚本:

如果PowerShell脚本与批处理脚本的工作方式相同,那么$MyVar(批处理语法中为%MyVar%)的值将从“我是一个全局变量!”更改为“我被脚本设置了!”,最后变成了“我被函数设置了!”在它再次显式更改或会话终止之前,它将一直留在该位置。但是,当我们遍历每个作用域时,请查看此处实际发生了什么-特别是在FunctionScope函数完成其工作并且我们再次从脚本检查变量之后,然后从全局作用域检查。

正如您可以看到的,当我们在脚本中移动时,变量似乎发生了更改,因为在FunctionScope函数完成之前,我们一直在上次更改的同一范围内检查变量。不过,在FunctionScope完成之后,我们又回到了脚本作用域,其中$MyVar没有被函数触及。然后,当脚本终止时,我们返回到全局作用域,在那里它根本没有被修改。

触及当地范围之外

因此,这一切都很好地帮助您避免意外地将更改应用到脚本和函数之外的环境中,但是如果您确实想要进行这样的修改呢?有一种特殊且相当简单的语法用于创建和修改超出Local作用域的对象。您只需将作用域名放在变量名的开头,并在作用域名和变量名之间加一个冒号。就像这样:

查看和设置变量时都可以使用这些修饰符。让我们看看此演示脚本会发生什么情况:

与前面一样,我们将从设置全局作用域中的变量开始,最后检查最终的全局作用域结果。

在这里您可以看到,FunctionScope能够更改脚本范围内的变量,并使更改在完成后保持不变。此外,即使在脚本退出之后,对全局作用域中的变量的更改仍然存在。如果您必须使用相同的代码重复更改脚本内或全局范围内的变量,则这可能特别有用-您只需定义一个函数或脚本,以便在需要修改变量的位置和方式进行修改,并在需要进行这些更改时调用该函数或脚本。

如前所述,作用域编号还可以在某些命令中使用,以修改与Local作用域相关的不同级别的变量。下面是上面第二个示例中使用的相同脚本,但对函数进行了修改,以便将get-variable和set-variable命令与作用域编号一起使用,而不是直接引用具有命名作用域的变量:

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

附加信息

使用作用域可以做的事情仍然比本文所能做的要多得多。作用域影响的不仅仅是变量,关于私有作用域和AllScope变量还有更多需要了解的内容。有关更多有用信息,您可以从PowerShell中运行以下命令:

TechNet上也提供了相同的帮助文件。

范围图像信用:openclipart上的spadassin