【问题标题】:Powershell local array re-initializationPowershell本地数组重新初始化
【发布时间】:2021-04-28 12:10:58
【问题描述】:

我在我的一个脚本中发现了一个错误,看起来 PowerShell(2.0 版)在再次输入函数时没有重新初始化本地数组。这是一个极简主义的例子:

function PrintNumbersUntilX {
    param (
        [ Parameter( Mandatory = $true ) ] $X
    )
    $Number = 0
    while( $Number -le $X ) {
        Write-Host $Number
        $Number++
    }
}

function PrintNumbersUntilY {
    param (
        [ Parameter( Mandatory = $true ) ] $Y
    )
    $Number = ( , 0 )
    while( $Number[ 0 ] -le $Y ) {
        Write-Host $Number[ 0 ]
        $Number[ 0 ]++
    }
}

function Main {
    Write-Host 'X-3: '
    PrintNumbersUntilX -X 3
    Write-Host 'X-12: '
    PrintNumbersUntilX -X 12
    Write-Host 'Y-3: '
    PrintNumbersUntilY -Y 3
    Write-Host 'Y-12: '
    PrintNumbersUntilY -Y 12
}

Main

输出(用空格替换换行符):

X-3:  0 1 2 3 X-12:  0 1 2 3 4 5 6 7 8 9 10 11 12 Y-3:  0 1 2 3 Y-12:  4 5 6 7 8 9 10 11 12

PrintNumbersUntilX 按我对局部变量的预期工作,但 PrintNumbersUntilY 不会重置(重新初始化)$Number

此错误是否已在以后的版本中修复,或者这是一个“功能”,如果是,它叫什么,我怎样才能摆脱它?

【问题讨论】:

  • 这是特定于 2.0 的(在以后的版本中可以以 -version 2 开头,但不是其他的),但即使在那里它也可以通过使用 $Number = @(0) 来修复(这是声明数组,即使偶尔需要逗号技巧来防止在更复杂的情况下展开)。
  • 关于 v2 特定的错误的好点,@JeroenMostert,但@(...) 不是在 PowerShell 中声明数组的正确方法,因为它通常是不必要的并且可能会引起概念上的混淆 - 请看我的回答。 , 0 在这种情况下不是技巧,它是正确使用数组构造运算符的一元形式来构造单元素数组。

标签: powershell local-variables


【解决方案1】:

您可以在 2.0 中通过使用显式数组子表达式 @() 来初始化 $Number 来解决它:

$Number = @( , 0 )

【讨论】:

    【解决方案2】:

    您知道这一点,但我们要明确一点,观察到的行为显然是一个 - 相当严重的错误 - 在 Windows PowerShell 2.0 版中

    • 看起来,将直接构造的单元素数组分配给局部变量在随后调用给定脚本或函数时“优化”了。

    • 如果可能,请考虑升级您的 PowerShell 版本:Windows PowerShell v2.0 于 2009 年 10 月发布;当前版本是 v5.1。

      • v2.0 肯定不会看到任何错误修复,而 v5.1 - 这是最后一个 Windows PowerShell 版本 - 是不再积极开发,但可能看到错误修复 - 取决于给定错误的严重程度。

      • 相比之下,跨平台的PowerShell (Core) v6+ 版本是Windows PowerShell 的继任者积极开发和维护


    解决方法:

    选项 1: 使用@(...)array-subexpression operator 代替 , 的一元形式,array constructor operator

    $Number = @( 0 )
    

    这是Mathias R. Jessen's solution 的简化版,或许不太晦涩。

    要明确:

    • 您构造单元素数组的方法 - , 0 - 在 v3+ 中可以正常工作,在概念上更可取,因为 - 与 , 运算符不同 - @(...) 实际上并不构造数组,它“保证”它们,松散地说:即@( @( 0 ) )@( , 0 )@( 0 ) 相同并产生一个(非嵌套)单元素数组。相比之下,, , 0 确实构造了一个 嵌套 数组(一个元素是另一个元素为 0 的单元素数组的单元素数组)。

    • @(...) 从未打算用于声明数组字面量:它的目的是收集包含的命令或表达式的输出 在新构建的[object[]] 数组中,即使只输出一个对象,从而保证情景管道输出的单个对象仍然被处理作为数组(假设单对象输出默认按原样收集,没有数组包装器)。

      • 在直到 v5.0 的 PowerShell 版本中,将 @(...) 与数组文字一起使用不仅不必要,而且效率低下额外 > 数组是根据所描述的机制在幕后创建的。

      • 由于@(...) 用于数组初始化的“标签外”使用非常普遍,v5.1 引入了优化:@(...)@( 1, 2 ) 等表达式的幕后被有效优化,使其有效同1, 2

      • 正是这种优化的缺席使得@( 0 ) 解决方法在v2.0 中有效:因为评估被推迟到运行时,错误的分配“优化”没有被执行。

    • 有关@(...)的更多信息,请参阅this answer的底部

    选项 2:类型约束变量作为数组,这与强类型 [int[]] 甚至提高了效率(虽然影响可以忽略不计):

    [array] $Number = 0 # Note: "," isn't even needed then; constructs [object[]] array
       
    # Or, more efficiently, with strong typing:
    # (The above constructs an object[] array in which value types must be boxed.)
    [int[]] $Number = 0
    

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 1970-01-01
      • 2016-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-14
      • 1970-01-01
      相关资源
      最近更新 更多