Powershell 字符串格式在循环时无法添加十六进制前缀

Powershell String format fails to add hex prefix when looping

再见-

我正在使用 Powershell 7.2 通过硬件的 CLI 自动执行一些硬件配置。 我正在使用一个循环来生成包含“0x”前缀的字符串以表示十六进制字节,但有一个问题,即在第一次循环之后的任何连续迭代都不会打印“0x”前缀。

以下会产生问题:

function fTest($id)
{
    foreach($n in @(1, 2, 3)) 
    {
        write-host $id.gettype()
        write-host ("{0:x}" -f $id)
        $id++
    }
}

fTest 0x1a

实际输出:

System.Int32
0x1a
System.Int32
1b
System.Int32
1c

迭代 2 和 3 中省略了 0x前缀。

我是 PowerShell 菜鸟,所以我很高兴收到完全不同方法的建议或示例。

在此先感谢您的帮助!

tl;dr

  • Type-constrain 您的 $p 参数 明确地 使其成为 数字 (整数),正如 Theo 所建议的那样:

    • function fTest($id) -> function fTest([int] $id)
  • 0x 前缀构建到传递给 -f 的格式字符串中:

    • "{0:x}" -f $id -> '0x{0:x}' -f $id

基于有用的评论:

Why is this happening?

  • 格式字符串{0:x},当应用于数字时,只会产生十六进制表示而没有 ]一个0x前缀;例如:

     PS> '{0:x}' -f 10
     a   # NOT '0xa'
    
    • 如果操作数 不是 数字,则数字 :x 规范被 忽略 :

       PS> '{0:x}' -f 'foo'
       foo
      
  • 您遇到的问题与 PowerShell 如何处理传递给非 type-constrained:

    参数的参数有关
    • 参数0x1a 不明确:它可能是一个数字 - 表示为十六进制常数0x1a,相当于十进制 26 - 或 string.

      • 虽然在表达式-解析模式中,这种歧义不会出现(字符串必须在那里被引用),它在argument- 解析模式,其中字符串周围的引号是可选的(除非字符串包含元字符)- 请参阅概念性 about_Parsing 主题。
    • PowerShell 在这种情况下所做的是创建 a hybrid 参数值:该值被解析为 number,但它在后台缓存其原始字符串表示,用于显示格式,例如:

      PS> & { param($p) $p; $p.ToString() } 0x1a
      0x1a  # With default output formatting, the original string form is used.
      26    # $p is an [int], so .ToString() yields its decimal representation
      
  • 从 PowerShell 7.2.2 开始,令人惊讶且有问题的是,-f 的上下文中,string-formatting operator,例如 混合值被视为字符串,即使它self-reports被视为数字

    PS> & { param($p) $p.GetType().FullName; '{0:N2}' -f $p } 0x1a
    System.Int32  # $p is of type [int] == System.Int32
    0x1a          # !! With -f $p is unexpectedly treated *as a string*,
                  # !! yielding the cached original string representation.
    
  • Type-constraining 将这种混合参数传递给的参数,如顶部所示,避免了歧义:在调用时,参数被转换为参数类型的未包装实例(见下一点)。

  • 至于为什么输出从第2次迭代开始改变:

    • 在这种情况下,缓存的字符串表示是通过 [psobject] 存储在 $id 中的数字类型实例的不可见 [psobject] 包装器实现的。

    • 当您通过递增操作 (++) 更新此值时,[psobject] 包装器 丢失 ,并且变量更新为一个展开的数字(原始值 + 1)。

    • 因此,从第 2 次迭代开始,$id 包含一个未包装的 [int] 实例,导致 {0:x} 数字格式被接受,因此产生一个十六进制表示 没有 0x 前缀。

    • 第一次迭代产生 0x 前缀的唯一原因是它出现在参数的原始字符串表示中;如上所述,在这种情况下 numeric :x 格式说明符被 忽略 ,因为 -f 操作数是 (意外) 被视为 string.