【问题标题】:Global variables: Arrays not behaving like other variables全局变量:数组的行为与其他变量不同
【发布时间】:2015-02-15 14:19:38
【问题描述】:

我有一个 PowerShell 脚本,它由一个主 PS1 文件组成,然后加载多个模块。在其中一个模块中,我定义了一个变量 $global:locationsXml ,然后在没有全局标志的情况下继续添加它,它工作得很好。我可以在没有任何其他模块的全局标志的情况下引用它。 但是,我还定义了一个 $global:loadedDefinitions = @() 数组并添加到其中。但是当我用 += 添加它时,我必须用全局标志来引用这个变量。我可以在没有全局标志的任何其他模块中引用它,但在创建模块中我需要它。该模块与 xml 变量的工作方式不同/正确的模块相同。 我还有一个哈希表,我在没有全局标志的情况下定义了它,但是在加载所有模块的顶级脚本中,我可以从任何地方在没有全局标志的情况下引用它。此外,我尝试在父脚本中初始化问题数组,如哈希表,但该数组仍然需要填充它的模块中的全局标志。但不在只读取它的不同模块中。 所有这些目前都在 Windows 7 和 PS 2.0 中进行测试。 所以,在我拆开东西之前,我想知道;是否存在已知错误,全局数组的行为与其他全局变量不同,特别是在模块中写入时? 我想包括用于写入我需要的几个数组的全局标志不会有什么大不了的,但我想了解发生了什么,特别是如果它是某种预期的行为而不是错误。

编辑:澄清一下,这是可行的

Script:
 Define Hash Table without global specifier;
 Load Module;
 Call Function in Module;
  Read and write Hash Table without global specifier;

这行得通

Script:
  Load Module;
  Call Function in Module;
    Initialize Array with global specifier;
    Append to Array with global specifier;
  Reference Array from anywhere else WITHOUT global specifier;

这不是

Script:
  Load Module;
  Call Function in Module;
    Initialize Array WITH global specifier;
    Append to Array without global specifier;
  Reference Array from anywhere fails;

这种方法,仅使用全局说明符初始化变量,然后在没有它的情况下引用它适用于其他变量,但不适用于数组,“似乎”是我看到的行为/错误。奇怪的是,全局说明符只需要在初始化 Array 的模块中使用,而不需要在任何其他模块中使用。我还没有验证它是否也只是在初始化它的函数中,和/或只是写入数组,而不是读取。

【问题讨论】:

  • 嗯,我觉得我的情况有点不同。我没有传递任何讨论过的变量,也没有任何同名的局部变量。我将编辑原始帖子以澄清,因为我可以在那里使用退货。
  • 我原来也是这么想的,但是我发现一旦一个变量是全局的,那么它在所有范围内都是可用的,因此不再需要全局说明符了。在我的情况下,我将所有内容作为从快捷方式启动的脚本运行,因此根本不涉及 ISE。事实上,我不需要使用全局,我应该能够使用脚本级别的范围,但我的理解是(有缺陷的?)模块的实现方式需要全局范围。如果您不使用模块,则不需要全局范围。
  • 我还需要查看更高版本。可能这种在没有说明符的情况下引用全局范围的能力本身就是一个错误,V3 或更高版本修复了它。
  • 经过测试,可在Win8.1/v4下运行。意思是在没有全局说明符的情况下读取全局变量。我可能会测试写入行为,但由于我的 80% 的客户仍在使用 Windows 7 并且不会很快更改或升级 PowerShell,如果这样做也没关系,我必须解决这个问题。

标签: debugging powershell global-variables


【解决方案1】:

当你从没有作用域说明符的变量中读取时,PowerShell首先在当前作用域中寻找变量,然后,如果没有找到,则转到父作用域,直到找到变量或到达全局作用域。当您写入没有范围说明符的变量时,PowerShell 仅在当前范围内写入该变量。

Set-StrictMode -Version Latest #To produce VariableIsUndefined error.
&{
    $global:a=1
    $global:a   #1
    $local:a    #  Error VariableIsUndefined.
    $a          #1 Refer to global, $a as no $a in current scope.
    $a=2        #  Create variable $a in current scope.
    $global:a   #1 Global variable have old value.
    $local:a    #2 New local variable have new value.
    $a          #2 Refer to local $a.
}

调用对象的方法、属性和索引器的访问器(包括集合访问器)只能从变量中读取。写入对象不同于写入变量。

Set-StrictMode -Version Latest #To produce VariableIsUndefined error.
&{
    $global:a=1..3
    $global:a-join',' #1,2,3
    $local:a -join',' #        Error VariableIsUndefined.
    $a       -join',' #1,2,3   Refer to global $a, as no $a in current scope.
    $a[0]=4;          #        Write to object (Array) but not to variable, variable only read here.
    $global:a-join',' #4,2,3   Global variable have different content now.
    $local:a -join',' #        And you still does not have local one.
    $a       -join',' #4,2,3   Refer to global $a, as no $a in current scope.
    $a+=5             #        In PowerShell V2 this is equivalents to $a=$a+5.
                      #        There are two reference to $a here.
                      #        First one refer to local $a, as it is write to variable.
                      #        Second refer to global $a, as no $a in current scope.
                      #        $a+5 expression create new object and you assing it to local variable.
    $global:a-join',' #4,2,3   Global variable have old value.
    $local:a -join',' #4,2,3,5 But now you have local variable with new value.
    $a       -join',' #4,2,3,5 Refer to local $a.
}

所以如果你想从非全局范围写入全局变量,那么你必须使用global 范围说明符。但是如果你只想从全局变量中读取,而不是被同名的局部变量隐藏,你可以省略global范围说明符。

【讨论】:

  • 该死,为什么我无法格式化评论?或者,更有可能的是,我错过了什么可以让我格式化 cmets? ;-) 无论如何...嗯,那我要做什么巫术,因为在我的父脚本中我有 $Px = @{ Version = '3.2.2' Context = 'Machine' ScriptPath = Split-Path $ ($script:MyInvocation.MyCommand.Path) ScriptName = $script:MyInvocation.MyCommand.Name Set = "" } 从任何模块中的任何函数我都可以毫无问题地执行 $Px.Set = "Some Value"。它在 v2 和 v4 中都以这种方式工作。
  • 现在,让事情变得更有趣,我有另一个数组,它在不同的模块中使用全局说明符初始化为空,我可以从任何模块中的函数读取和写入它。因此,这似乎不是专门针对 Arrays 的问题。有效的数组在模块顶部初始化,在任何函数之外,所以我尝试对我的问题数组使用相同的方法,但没有任何乐趣。可以肯定的是,我的行为就像我有一个同名的数组,它要么是本地的,要么是脚本范围的,但如果我能找到它,那该死的。啊!!!
  • 您应该使用有效的 powershell 示例更新您的问题,以说明写入数组的工作原理和不工作的地方。当您将元素添加到数组中时,例如$arr += $item,您将创建一个新对象并将其保存在$arr 变量中,该变量是在本地范围内创建的,就像@PetSerAl 所示。 $Px.Set = "lala" 起作用的原因是因为您实际上只是在读取变量而不是写入变量。您正在读取对对象的引用,并修改对象的属性而不将任何内容保存到变量本身 = 没有范围问题。
  • 另外,在 cmets 中使用反引号 ` 添加代码块。对于大样本,编辑您的问题并将其插入其中。评论应该简短而甜蜜:)
  • 我将尝试用更具体的内容更新示例。但是我在 2 个 PS1 和 3 个模块文件中拥有 10K+ 行代码,以及大量的函数,所以还不确定如何在完整记录问题的同时简单地记录问题。最后,我想知道最好的做法是不是总是使用全局说明符。我曾经看到将说明符用作语法糖的能力,也许它没有我想象的那么甜。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-06
  • 1970-01-01
  • 2021-11-06
  • 2021-09-07
  • 2012-08-25
  • 1970-01-01
相关资源
最近更新 更多