当前位置:网站首页 > 更多 > 玩电脑 > 正文

[玩转系统] 使用 PowerShell 变量范围构建更好的脚本

作者:精品下载站 日期:2024-12-14 20:51:40 浏览:11 分类:玩电脑

使用 PowerShell 变量范围构建更好的脚本


当您编写没有函数且不依赖于其他脚本的 PowerShell 脚本时,PowerShell 作用域的概念并不重要。 PowerShell 全局变量的概念并不是最重要的。但当您开始构建函数、模块并学习从其他脚本调用脚本时,这个主题就变得更加重要。

在本文中,您将深入了解 PowerShell 中的作用域是什么、它们如何工作以及如何在考虑作用域的情况下编写代码。完成后,您将了解 PowerShell 的全局变量以及更多内容!

范围:有点像水桶

您是否曾经编写过一个脚本,其中定义了一个变量,但当您检查该变量的值时,却发现是其他值?当您明确定义该变量时,您可能会摸不着头脑该变量是如何变化的。一个原因可能是变量的值在另一个作用域中被覆盖。

也许您想知道某些 PowerShell 变量在控制台中引用时如何具有值,但在脚本中并不存在。这些变量很可能位于另一个当时不可用的“桶”中。

范围就像水桶。范围影响 PowerShell 在不同区域之间隔离变量、别名、函数和 PSDrive 的方式。示波器就像一个水桶。这是一个将所有这些物品收集在一起的地方。

当 PowerShell 启动时,它会自动为您创建这些“存储桶”。那时,您已经在不知不觉中使用了范围。所有范围均由 PowerShell 定义,并且无需您的任何帮助即可创建。

范围类型

当 PowerShell 启动时,它会自动创建四个“桶”或范围来放置各种项目。您不能自己创建范围。您只能在下面定义的这些范围中添加和删除项目。

全球范围

PowerShell 打开时定义的项目是在全局范围内设置的。这些项目包括系统创建的对象(例如 PowerShell 驱动器)以及自配置文件在启动时运行以来在 PowerShell 配置文件中定义的任何内容。

全球范围内的物品随处可用。您可以在控制台、运行的任何脚本以及任何函数中以交互方式引用全局范围内的项目。 PowerShell 的全局变量无处不在。因此,PowerShell 全局变量的常见用法是在脚本之间使用 PowerShell 全局变量。

只有一个全局作用域负责全局。

脚本范围

每次运行 PowerShell 脚本时都会自动创建脚本作用域。您可以有许多不同的脚本范围实例。例如,脚本作用域是在执行 PS1 脚本或模块时创建的。

只有在该特定脚本范围实例中创建的项目才能相互引用。

私有范围

通常,定义的项目可以从其他范围访问 - 对于私有范围中的项目则不然。私有范围中的项目包含对其他范围隐藏的对象。私有范围用于创建与其他范围中的项目同名的项目,以防止重叠。

本地范围

与其他作用域不同,本地作用域有点不同。本地作用域是指向全局、脚本或私有作用域的指针。本地范围与代码当时运行的任何上下文相关。

如果您创建变量、别名、函数或 PSDrive 时没有显式为其分配作用域(我们稍后将介绍),它将进入本地作用域。

参考范围

现在您已经了解了存在的四种类型的作用域,您还应该知道有两种方法来引用这些作用域。

在 PowerShell 中,有两种引用作用域的方法:命名或编号。两种方法都引用相同的范围,但只是以不同的方式。这是与示波器交互的两种不同方式。

命名范围

在上面的范围类型部分中,您了解了按名称引用的范围。直观上,通过名称引用作用域称为命名作用域。按名称引用范围的主要目的是将项目分配给范围。您将在下面了解如何执行此操作。

编号范围

除了名称之外,每个作用域都有一个从零开始的数字,该数字始终是本地作用域。范围根据当前本地范围动态编号。

例如,一旦您打开 PowerShell 会话,您就可以在全局范围内进行操作。此时,全局作用域就是局部作用域(记住局部作用域只是一个指针)。

由于局部作用域始终为零作用域,因此此时全局作用域当前也为零作用域。但是,当您从同一会话运行脚本时,就会创建脚本作用域。运行时,本地作用域指针已更改为脚本作用域。现在,脚本作用域是作用域零,全局作用域是作用域一。

范围已编号 对于本地范围为 0 的范围,此过程会重复进行。范围按范围层次结构动态编号。

范围层次结构和继承

如前所述,当您启动 PowerShell 会话时,PowerShell 会在全局范围内为您创建一些项目。这些项目可以是函数、变量、别名或 PSDrive。您在 PowerShell 会话中定义的任何内容也将在全局范围内定义。

由于默认情况下您处于全局作用域中,因此如果您执行创建另一个作用域的操作(例如执行脚本或运行函数),则会创建一个子作用域,其中父作用域是全局作用域。范围就像父母和孩子的流程。

在父作用域(在本例中为全局作用域)中定义的任何内容都可以在子作用域中访问。但这些项目只能在它们定义的范围内编辑。

假设您有一个名为 Test.ps1 的脚本。该脚本的内部是一行,如下所示。

$a = 'Hello world!'

当您运行此脚本时,$a 会在本地范围(脚本运行时的脚本)中分配一个值。当Test.ps1运行时,您可以在下面看到脚本执行后无法引用它。由于 $a 是在脚本作用域中分配的,因此全局作用域(在交互式控制台)看不到它。

让我们更进一步,使 Test.ps1 脚本如下所示。该脚本现在尝试输出 $a 的值,然后再将其设置在相同的范围内。

Write-Output $a
$a = 'Hello world!'

为了进行演示,请在交互式控制台上为 $a 分配一个值。这会在全局范围内分配值。现在,脚本运行,它将继承父范围(全局)并且应该能够看到该值。

您可以在下面看到,当执行 Test.ps1 时(创建全局作用域的子作用域),它可以看到 $a 的值。您还可以看到变量的值在全局范围内也可用,因为该范围是它的设置位置。这意味着 $a 在脚本(子级)和父级(全局)作用域中都可用。

记住这个作用域继承行为。这样做将帮助您解决发生变量冲突的情况,例如不同范围内的变量具有相同的名称。

定义和访问范围中的项目

既然您知道什么是作用域以及它们如何工作,那么如何访问它们呢?让我们看看如何使用 PowerShell 设置变量范围(并访问它们)。

获取/设置变量

在 PowerShell 中,有两个 cmdlet 允许您设置名为 Get-VariableSet-Variable 的变量。这些 cmdlet 允许您检索变量的值或定义值。

这两个 cmdlet 都具有类似的 NameScope 参数。使用这些参数,PowerShell 允许您在所有范围内设置和检索变量值。

本地范围

要在本地范围内设置变量,请使用 Set-Variable 并为其提供本地变量名称和值,如下所示。

PS> Set-Variable -Name a -Value 'foo'

本地作用域始终是默认值,因此不使用 Scope 参数将始终在本地作用域中定义变量。

要检索本地范围变量的值,请使用 Get-Variable 为其提供名称。

PS> Get-Variable -Name a

Name         Value
----         -----
a            foo

私有/脚本/全局范围

在处理私有变量、脚本变量和全局变量时,您也将使用相同的参数(NameValue)。唯一的区别是这次您将使用 Scope 参数来显式定义范围。

设置私有变量、脚本变量或全局变量的方法是相同的。只需将传递给 Scope 参数的值替换为 PrivateScript Global

PS> Set-Variable -Name a -Value 'foo' -Scope <Private|Script|Global>

要检索脚本或全局范围变量的值,请使用 Get-Variable 为其提供名称和范围。

PS> Get-Variable -Name a -Scope <Script|Global>

Name         Value
----         -----
a            foo

注意:您还可以通过 Get/Set-Variable cmdlet 使用范围编号而不是名称来引用范围。

范围“序言”

您还可以使用快捷方式检索和设置范围中的变量。您无需使用 PowerShell cmdlet,而是在引用变量时在其范围前面加上前缀。

本地范围

由于本地作用域始终是默认值,因此只需定义变量并引用即可设置和检索本地作用域变量

PS> $a = 'foo'
PS> $a

foo

私有/脚本/全局范围

如果您想定义和引用脚本或全局作用域,您可以在变量前面加上作用域名称和分号。

例如,要在全局范围内设置变量 $a,您可以在 a 前面添加 $global:

$global:a = ‘foo’

对于脚本范围的变量也可以执行相同的操作。

$script:a = ‘foo’

一旦变量设置在首选范围内,您就可以以相同的方式引用它们。另请注意,如果定义的范围是本地范围,则可以排除范围前言。

PS> $global:a = 'foo'
PS> $global:a
foo
PS> $a
foo

脚本块中的范围

PowerShell 有一个方便的构造,称为脚本块。脚本块允许您移动代码片段并在任何地方执行它们。

与 PS1 脚本一样,脚本块在其自己的脚本范围内运行。当您执行脚本块时,您实际上是在执行 PS1 脚本。

请注意,在下面的示例中,变量在全局作用域中定义,然后尝试在脚本作用域中覆盖。正如您在上面了解到的,这是行不通的,因为子作用域无法引用父作用域。

此示例显示,当脚本块中的 $a 发生更改时,$a全局变量定义不会更改,因为脚本块是一个脚本子范围。

点采购脚本(交换本地范围)

PowerShell 有一个称为点源的概念。这是一种允许您执行 PS1 脚本并将所有脚本范围内的内容放入本地范围的方法。

通过在引用 PS1 脚本并执行它之前放置一个点 (.),该“点源”脚本的内容并将所有内容带入本地范围。

为了进行演示,我再次有一个 Test.ps1 脚本,它定义了一个变量,如下所示。

$a = 'Hello world!'

在 PowerShell 控制台中,为 $a 变量设置一个值,然后点源该脚本,如下所示。请注意,原始变量已被覆盖。 PowerShell 将两个本地作用域“合并”在一起。

使用 AllScope 属性(选项)

您已经了解了如何与特定范围内的项目进行交互,但到目前为止,每个项目仍然在单个范围中定义。但是,如果您不知道变量定义的范围怎么办?

使用 Set-Variable cmdlet 定义变量时,您可以一次将该变量放入所有作用域中。为此,请使用 Option 参数的 AllScope 值。

为了进行演示,Test.ps1 脚本已被修改为在所有范围内设置 a 变量。然后输出该变量,如下所示。

Set-Variable -Name a -Value 'Hello world!' -Option AllScope
Write-Output $a

然后您可以在下面看到在全局范围内为 $a 设置了一个值,并且执行了 Test.ps1 脚本。然而,$a 的值并没有生效,而是被覆盖了。它不仅在脚本作用域 (Write-Output $a) 中定义,而且还覆盖了全局作用域。

AllScope 选项很方便,但要小心。此选项本质上消除了范围的概念并将所有内容合并在一起。

函数范围

当您执行函数时,该函数内的所有代码都位于其自己的子作用域中。函数作用域遵循与其他作用域相同的子/父行为。

为每个函数设置单独的作用域是一个好主意。它可以更好地控制项目,而不必担心项目冲突,它还提供了自动清理函数中变量的额外好处。一旦函数完成,函数中定义的所有项目都将被清除。

为了进行演示,请将以下函数直接复制/粘贴到 PowerShell 控制台中。

Function Do-Thing {
    $var = 'bar'
}

粘贴后,执行该函数。请注意,您无法在函数外部访问 $var 变量。

PS> Do-Thing
PS> $var

保持项目私有(禁用继承)

通常,如果在父作用域中定义变量,则该变量也将在子作用域中定义。但也许您想使用变量名,但该变量名已在要在会话中运行的作用域之一中定义。在这种情况下,您可以选择不同的变量名称或在私有范围中定义变量,使其成为私有变量。

使用作用域减少冲突的一种方法是使用私有作用域。使用私有作用域会禁用该特定变量的继承。创建多个子作用域时,这些子作用域将看不到私有作用域中定义的任何变量。

Test.ps1 脚本输出 $a 的值,如下所示。

Write-Output $a

您可以在下面看到我在全局范围内定义了一个私有范围的变量,然后执行 Test.ps1 脚本。通常,当您在父作用域中定义变量时,该变量将在子作用域中可用,而私有作用域变量则不然。

在下面的示例中,您可以看到通过执行 Test.ps1 脚本创建的脚本子作用域无法看到父作用域中定义的私有作用域 $a 变量。

与全局作用域或 Set-Variable cmdlet 上的 AllScope 选项不同,私有作用域变量是划分项目的绝佳方法。

确定最佳实践范围

认为在全局范围内定义变量或使用 AllScope 选项是可行的方法是很常见的。毕竟,所有变量随处可用。无需担心示波器的复杂性。虽然这确实为访问定义的内容提供了额外的自由,但它可能很快就会失控并且难以排除故障。

不要试图阻止使用范围,请遵循以下提示:

  1. 使用参数将所需信息传递给函数,而不是在函数中指定范围。
  2. 尽可能保持在本地范围内。
  3. 不要从脚本定义全局变量,而是使用 Write-Output cmdlet 输出所有内容,并在需要时从控制台将其保存到变量中。

这里的要点是拥抱范围并学会利用它们来发挥自己的优势,而不是试图规避它们。

进一步阅读

  • 关于高级函数和函数参数
  • 在 PowerShell 中使用环境变量

您需要 登录账户 后才能发表评论

取消回复欢迎 发表评论:

关灯