【问题标题】:Array being turned into string in hash table数组在哈希表中变成字符串
【发布时间】:2019-03-22 15:24:55
【问题描述】:

我有一个返回给我的数组,我正在使用 foreach 逐步执行该数组,我想在哈希表中使用该数组的每个成员的一个元素,该哈希表将被转换为 json 对象。当我将元素添加到 powershell 数组对象,然后将其添加到哈希时,字符串被组合而不是添加为数组。

规格

"destinationInterclusterLifIps": [
  "172.31.5.119",
  "172.31.15.103"
]

实际回报:

"destinationInterclusterLifIps":  [
     "172.31.33.150172.31.42.41"
 ],

构建数组

if(!$destinationInterclusterLifIps){
    $destinationInterclusterLifIps =  @()
    foreach($record in $interclusterIpInfo.peerInterClusterLifs){
        $destinationInterclusterLifIps += $record.address
    }
}

哈希表

$body = @{
        destinationInterclusterLifIps = @($destinationInterclusterLifIps)
}

对于我缺少的哈希表来说,这必须是一些简单的事情

【问题讨论】:

  • PowerShell 的 auto-collection-feature-thingy 可以将其减少到仅 $interclusterIpInfo.peerInterClusterLifs.address 而无需循环。
  • @JeroenMostert:不幸的是,那个东西在官方文档中没有名字;我们所拥有的最接近官方名称的是blog post that announced the feature in v3 中的成员枚举。这是我在 SO 上的答案中一直使用的名称,特别是在 this one 中,它试图全面解释该功能。
  • 不知道这一点。我不是这个名字的忠实粉丝——你知道,列举各个成员听起来很模棱两可,但我想总比没有好。
  • 在这两个方面都同意,@JeroenMostert。文档现在至少 describe the behavior(表面上),但没有给它一个名字,所以至少假设可以选择一个更好的名字。
  • 转念一想,@JeorenMostert:想出既富有表现力又简洁的名字是很困难的,member enumeration 也不错:enumeration i> 涵盖了对集合的所有元素进行操作的方面,而 member 涵盖了每个元素上正在访问的内容。

标签: arrays string powershell type-conversion


【解决方案1】:

您的代码本身并不能解释症状,但your own answer 暗示了一个值得探索的有趣的陷阱

  • 默认情况下,PowerShell变量可以接受任何类型的值,并且可以随时分配不同类型的值:

    $var = 42   # $var now contains an [int]
    # ...
    $var = 'hi' # $var now contains a [string]
    
  • 但是,您可以类型约束变量,在这种情况下,它们只接受该类型的值 - 或者可以自动转换的值 em> 到那种类型(PowerShell 在自动转换方面非常灵活):

    # Create a type-constrained variable.
    [int] $var = 42  # $var now contains an [int] AND is locked into storing [int]s
    
    # Try to assign a [string] that CANNOT be converted to an [int]
    $var = 'hi' # FAILS: 'Cannot convert value "hi" to type "System.Int32"...'
    
    # Try to assign a [string] that CAN be converted to an [int]
    $var = ' 42  ' # OK - string was converted; $var now contains [int] 42
    

通常,但不一定,参数变量是类型受限的,作为脚本或函数声明参数列表的一部分。

如果您稍后重用一个受类型约束的参数变量,通过为其分配一个新值,它将强制执行其原始类型,如上所述。

在您的情况下,最可能的解释是您的 $destinationInterclusterLifIps 被声明为类型受限的 [string] $destinationInterclusterLifIps 参数,在这种情况下,稍后尝试分配一个 数组 导致该数组的隐式字符串化;用一个简单的例子来说明:

function foo {
  param(
   # Type-constrained parameter.
   [string] $destinationInterclusterLifIps
  )

  # ...

  # Try to assign an *array*:
  # Instead of storing an array, the [string] type constraint 
  # converts the array to a *single string*, as a space-separated list of
  # its elements.
  $destinationInterclusterLifIps = 1, 2, 3

  # Output the value.
  $destinationInterclusterLifIps

}

# Call the function (no argument needed)
foo

这个输出:

1 2 3

即,将数组转换为字符串,作为其元素的空格分隔列表。 (数组将逐个元素打印,每个元素在自己的行上)。

请注意,您的问题因此与 哈希表 的使用及其到 JSON 的转换无关 - 这正是您碰巧注意到问题的地方。


可选阅读:修改/删除变量的类型约束

以后可以重新创建具有不同类型的类型约束变量;例如:

[int] $var = 42; [string] $var = 'hi'

虽然您可以使用Remove-Variable 来有效地删除类型约束,方法是先删除变量然后不受约束地重新创建它,正如TheIncorrigible1 建议的那样 -
[int] $var = 42; Remove-Variable var; $var = 'hi' - 你可以使用[object] $var = 'hi' 作为快捷方式,因为对[object] 的类型限制等于限制值。

(Get-Variable var).Attributes 包含变量的属性,类型约束属性存储为[System.Management.Automation.ArgumentTypeConverterAttribute] 实例。如果您不关心删除 all 属性,另一个简单但晦涩的删除类型约束的选项是使用 (Get-Variable var).Attributes.Clear()

【讨论】:

  • 谢谢。 [psobject] 不是为了提高性能而首选的,因为不需要对 [object] 进行装箱/拆箱吗?
  • @TheIncorrigible1:What is [psobject]?Never heard of it。但是,说真的,我更喜欢在我的 PowerShell 代码中明确使用 [psobject] - 我不会担心这里的性能,但令人惊讶的是,快速测试显示使用 [psobject] 实际上会减慢速度:$a=1..1e5; tcm { foreach($i in $a) { [object] $v = 1 } }, { foreach($i in $a) { [psobject] $v = 1 } } ; tcm = gist.github.com/mklement0/9e1f13978620b09ab2d15da5535d1b27
【解决方案2】:

看起来我与变量发生了冲突。也许是因为它也被用作参数。将变量设置为任何其他名称即可解决此问题。

if(!$sourceInterclusterLifIps){
    $InterclusterIPSource =  @()
    foreach($record in $interclusterIpInfo.interClusterLifs){
        $InterclusterIPSource += $record.address
    }
} else {
    $InterclusterIPSource = $sourceInterclusterLifIps
}

【讨论】:

    【解决方案3】:

    尝试使用带有 .add() 的 ArrayList,它应该可以满足您的需求。

    $destinationInterclusterLifIps = New-Object System.Collections.ArrayList
    if(!$destinationInterclusterLifIps){
    $destinationInterclusterLifIps =  @()
    foreach($record in $interclusterIpInfo.peerInterClusterLifs){
        $destinationInterclusterLifIps.add( $record.address )
    }
    

    }

    【讨论】:

    • 虽然这可能更有效(尽管在这种情况下几乎无关紧要),但它既没有解释 Nicholas 的问题,也没有提供解决方案,因为它在功能上等同于原始代码(除了对于特定的集合类型,但这并不重要)。
    猜你喜欢
    • 1970-01-01
    • 2014-08-12
    • 2014-09-26
    • 1970-01-01
    • 2017-05-31
    • 2019-10-07
    • 2013-12-19
    • 2019-07-29
    • 2014-07-25
    相关资源
    最近更新 更多