17

我在 PowerShell 周围的数组和双引号中发现了一些奇怪的行为。如果我创建并打印数组中的第一个元素,例如:

$test = @('testing')
echo $test[0]

Output:
testing

一切正常。但是如果我在它周围加上双引号:

echo "$test[0]"

Output:
testing[0]

仅评估了 $test 变量,并且数组标记 [0] 被视为字符串。简单的解决方法是避免在双引号中插入数组变量,或者先将它们分配给另一个变量。但这种行为是设计的吗?

4

3 回答 3

30

因此,当您使用插值时,默认情况下它只会将下一个变量插值。所以当你这样做时:

"$test[0]"

它将 $test 视为下一个变量,它意识到这是一个数组并且它没有显示数组的好方法,因此它决定它不能插值并且只是将字符串显示为字符串。解决方案是明确告诉 PowerShell 插入的位从哪里开始以及在哪里停止:

"$($test[0])"

请注意,这种行为是我使用格式化字符串而不是依赖插值的主要原因之一:

"{0}" -f $test[0]
于 2012-02-08T15:47:24.683 回答
10

在这种情况下,您必须这样做:

echo "$($test[0])"

另一种选择是使用字符串格式

echo "this is {0}" -f $test[0]

请注意,当您访问字符串中的属性时也会出现这种情况。像"$a.Foo"- 应该写成"$($a.Foo)"

于 2012-02-08T15:48:41.940 回答
10

EBGreen 的有用答案包含有效的解决方案,但只是粗略解释PowerShell 的字符串扩展(字符串插值)

  • 只有变量本身可以直接嵌入引号字符串 ( "...")中(相比之下,单引号字符串 ( '...') 与许多其他语言一样,用于文字内容)。

    • 这适用于常规变量和引用特定命名空间的变量;例如:
      "var contains: $var","Path: $env:PATH"

    • 如果变量名后的第一个字符可能被误认为是名称的一部分(尤其是包括:) ,请在变量名周围使用{...}以消除歧义;例如:
      "${var}","${env:PATH}"

    • 要将a$用作文字,您必须使用`PowerShell 的转义字符对其进行转义;例如:
      "Variable `$var"

  • 变量名之后的任何字符- 包括[.都被视为字符串的文字部分,因此为了索引嵌入变量 ( $var[0]) 或访问属性( $var.Count),您需要$(...),子表达式运算符(实际上,$(...)允许您嵌入整个语句);例如:

    • "1st element: $($var[0])"
    • "Element count: $($var.Count)"
    • "Today's date: $((Get-Date -DisplayHint Date | Out-String).Trim())"
  • 字符串化(到字符串转换)应用于任何不是字符串的变量值/评估结果:

    • 警告:在可以应用文化特定格式的地方,PowerShell 选择不变文化,这在很大程度上与美国英语日期和数字格式一致;也就是说,日期和数字将以类似美国的格式表示(例如,月首日期格式和.小数点)。
    • 本质上,该.ToString()方法会在任何生成的非字符串对象或集合上调用(严格来说,它是,在某些情况下 .psobject.ToString()会覆盖,特别是对于数组/集合和 PS 自定义对象).ToString()
      • 请注意,这与直接输出变量或表达式时得到的表示不同,并且许多类型没有有意义的默认字符串表示 - 它们只是返回其完整的类型名称
        但是,您可以嵌入$(... | Out-String)以显式应用 PowerShell 的默认输出格式

      • 有关字符串化的更全面讨论,请参阅此答案


如前所述,使用-f,字符串格式化运算符 ( <format-string> -f <arg>[, ...])是字符串插值的替代方法,它将字符串的文字部分与变量部分分开:

'1st element: {0}; count: {1:x}'  -f  $var[0], $var.Count
  • 注意LHS 上的使用'...',因为格式字符串(模板)本身就是一个文字。在这种情况下使用'...'是一种良好的形成习惯,既表明使用文字内容的意图,也表明嵌入$字符而不转义的能力。

  • 除了简单的位置占位符({0}对于第一个参数。{1}对于第二个,...),您可以选择对字符串转换进行更多格式控制;在上面的示例中,x请求数字的十六进制表示。
    有关可用格式,请参阅运算符所基于的 .NET 框架String.Format方法的文档。-f

  • 陷阱:-f具有高优先级,因此请务必将除简单索引或属性访问之外的 RHS 表达式括在(...); 例如,'{0:N2}' -f 1/3不会按预期工作,只会'{0:N2}' -f (1/3)

  • 注意事项:字符串插值和-f

    • 与内部扩展不同"..."-f运算符文化敏感:

      • 因此,以下两个看似等效的语句不会 产生相同的结果:

             PS> [cultureinfo]::CurrentCulture='fr'; $n=1.2; "expanded: $n"; '-f: {0}' -f $n
        
             expanded: 1.2
             -f: 1,2
        
      • 请注意,只有-f-formatted 命令尊重法语 ( fr) 小数点 ( ,)。
        同样,请参阅先前链接的答案,全面了解 PowerShell 何时对文化敏感,何时不敏感。

    • 与内部扩展不同"..."-f将数组字符串化为<type-name>[]

         PS> $arr = 1, 2, 3; "`$arr: $arr"; '$arr: {0}' -f (, $arr)
      
          $arr: 1 2 3
          $arr: System.Object[]
      
      • 请注意"..."插值如何创建所有数组元素的字符串化的空格分隔列表,而-f-formatting 仅打印数组的类型名称。
        (如前所述,$arrinside"..."等价于:并且它是提供友好表示
        (1, 2, 3).psobject.ToString()的通常不可见的帮助器类型。)[psobject]

      • 另请注意如何(, ...)将数组包装$arr在辅助数组中,以确保-f将表达式视为单个操作数;默认情况下,数组的元素将被视为单独的操作数。

于 2018-05-07T13:23:56.470 回答