使用类型分配添加成员怪异

add-member weirdness with type assignment

在下面的代码中,为什么 name1 的 [int] 类型赋值被忽略并且实际上被视为值的一部分?错误?

$name1 = 4
$name2 = 4
$name3 = 4

$myObject = New-Object System.Object
$myObject | Add-Member -type NoteProperty -name name1 -Value [int]$name1
$myObject | Add-Member -type NoteProperty -name name2 -Value $($name2 -as [int])
$myObject | Add-Member -type NoteProperty -name name3 -Value $([int]$name3)

$myObject

输出:

name1  name2 name3
-----  ----- -----
[int]4     4     4

Powershell 版本:

get-host | select-object version

Version       
-------       
5.1.19041.1023

来自about_Parsing help file

Argument mode

When parsing, PowerShell first looks to interpret input as an expression. But when a command invocation is encountered, parsing continues in argument mode.

Argument mode is designed for parsing arguments and parameters for commands in a shell environment. All input is treated as an expandable string unless it uses one of the following syntaxes:

...

您可以将代码包含在子表达式 ($(...)) 中以避免 PowerShell 将 [int]$name1 视为可扩展字符串,正如您已经发现的那样:-)

这是设计使然。您需要用括号括起来,以便在将表达式传递给函数之前对其求值。

例如,如果您尝试将函数调用放在那里是一样的:

$myObject | Add-Member -type NoteProperty -name name1 -Value Write-Host

returns 为:

name1
-----
Write-Host

Write-Host [string]2returns[string]2不是2

现有答案中有很好的信息,但让我尝试进行系统概述:

tl;dr

为了传递表达式(例如[int] $name1)的输出,或(嵌套)命令(例如Get-Date -Format yyyy作为参数command(例如Add-Member),将其括在(...)中,grouping operator:

Add-Member -type NoteProperty -name name1 -Value ([int] $name1)

相比之下,$(...)subexpression operator is typically not needed in this scenario, and its use can have side effects - see .

  • 简而言之:(在 expandable strings"..." 之外),您只需要 $(...) 来包含 语言语句 (例如 if 语句或 foreach 循环)或 多个 语句(用 ; 分隔的命令、表达式和语言语句的任意组合)。

PowerShell 的解析模式:

PowerShell 有两种 基本解析模式:

  • argument 模式,其工作方式类似于shells.

    • 在参数模式中,第一个标记被解释为命令名称(cmdlet 的名称、函数、别名或外部可执行文件或 .ps1 脚本的路径名称),后跟 空格分隔列表 参数 ,其中字符串可以是 unquoted[1] 并且由文字部分和变量引用混合组成的参数是 通常 被视为可扩展字符串(就像它们包含在 "..." 中一样)。
  • 表达式模式,其工作方式类似于编程语言,其中字符串必须被引号,以及运算符语言语句,例如赋值,foreachwhile 循环,casts 可以使用。

概念性的 about_Parsing 提供了对这些模式的介绍;简而言之,它是给定上下文中的第一个标记,它决定应用哪种模式。

给定的语句可能由以任一模式解析的部分组成,这确实是上面发生的情况:

  • 因为你的语句以命令名Add-Member)开头,所以在参数[=224=中解析]模式。

  • (...) 强制一个新的解析上下文,在手头的情况下 ([int] $name1) 在 expression 模式下解析,由于以 [).

    开头

什么被认为是 元字符(具有特殊句法含义的字符)在解析模式之间存在差异

  • [= 仅在 expression 模式中是特殊的,而不是在 argument 模式,它们被使用的地方 verbatim.

  • 相反,token-initial @ 后跟变量 name 仅在 argument[=224= 中是特殊的] 模式,用于参数 splatting.

复合参数 [int]$name1 因此被视为 可扩展字符串 ,并导致逐字字符串 [int]4.

某些表达式在用作命令参数时 不需要 包含在 (...) 中(假设 $var = 'Foo') :

  • 一个独立变量引用(例如Write-Output $varWrite-Output $env:OS
  • 属性 访问 这样的引用(例如 Write-Output $var.Length
  • 方法调用 这样的引用(例如 Write-Output $var.ToUpper()

请注意,这些参数以其原始数据类型传递,未进行字符串化(尽管字符串化可能由接收命令执行)。

陷阱:

  • 您有时需要显式使用 "..." 以抑制 属性 访问解释并让 . 跟随变量引用逐字解释(例如Write-Output "$var.txt" 为了得到逐字 foo.txt).

  • 如果您使用 $(...) 作为复合参数 的一部分而没有 显式 "..." 引号,则该参数被分解为 多个参数如果$(...)子表达式启动参数(例如,Write-Output $('a' + 'b')/c传递两个 参数,逐字 ab/c,而 Write-Output c/$('a' + 'b') 只传递了一个,逐字 c/ab).

  • 类似地,只有当参数以 unquoted 标记(例如,Write-Output One"$var"'$Two'按预期工作并逐字生成 OneFoo$Two,但 Write-Output 'One'"$var"'$Two' 作为 三个 参数传递,逐字 OneFoo$Two).

简而言之:

  • 参数解析的确切规则很复杂:

    • 总结了 未引用 参数的规则。
    • 总结了在单个参数中混合引用和未引用的字符串
    • This answer)(底部)概括地总结了 PowerShell 的字符串文字。
  • 为了安全起见,避免在 "..." 之外使用 $(...) 并避免在单个字符串参数中混合引用样式;在表达式中使用(单个)"..." 字符串(例如 Write-Output "$(Split-Path $PROFILE)/foo.txt" 或 )或字符串连接(Write-Output ('One' + $var + '$Two')


[1] 假设它们既不包含空格也不包含任何 PowerShell 的元字符(参见 )。虽然引用通常采用将整个参数括在单引号或双引号中的形式(例如 'foo bar'"foo $var"),但也可以引用(转义)individual 个字符(例如 foo` bar),使用反引号 (`),PowerShell 的转义字符。