powershell 类型赋值的添加成员异常

afdcj2ne  于 2023-05-29  发布在  Shell
关注(0)|答案(3)|浏览(136)

在下面的代码中,为什么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
niwlg2el

niwlg2el1#

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

tl;dr

为了传递 * 表达式的输出(例如[int] $name1$env:HOME + '\foo')、或(嵌套) 命令**(例如Get-Date -Format yyyy作为 * 命令的 * 参数 *(例如Add-Member),将其括在(...),分组运算符:

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

相比之下,$(...),子表达式运算符在这种情况下通常 * 不 * 需要,并且它的使用可能会产生副作用-参见this answer

  • 简而言之:(除了expandable strings"..."),您只需要$(...)包含一个 *language语句 *(例如if语句或foreach循环)或 * 多个 * 语句(用;分隔的命令,表达式和语言语句的任何混合)。
PowerShell解析方式:

PowerShell有*两种 * 基本解析模式

参数 * mode,工作方式类似 * shell***。

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

概念性的about_Parsing提供了对这些模式的介绍;简而言之,*它是给定上下文中的 * 第一个令牌 ,它决定应用哪种模式。
一个给定的语句可以由以任何一种模式解析的部分组成,这确实是上面发生的事情:

  • 由于语句以 command nameAdd-Member)开头,因此将以 argument 模式进行解析。
  • (...)强制一个新的解析上下文,在本例中([int] $name1)是以 expression 模式解析的,因为是从[开始的)。
    元字符 (具有特殊语法意义的字符) 在不同的解析模式下有所不同:
  • [=仅在 expression 模式下是特殊的,而在 argument 模式下则不是,在 * argument* 模式下,它们是 * 逐字 * 使用的。
  • 相反,一个令牌初始值@后跟一个变量 name 只有在 argument 模式下才是特殊的,在这种模式下,它用于参数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总结了 * 无引号 * 参数的规则。
  • This answer总结了在单个参数中混合使用带引号和不带引号的字符串
  • This answer)(底部)概括了PowerShell的字符串字面量。
  • 为了安全起见,避免在"..."之外使用$(...),并避免在单个字符串参数中混合使用引号样式;或者使用(单个)"..."字符串(例如,Write-Output "$(Split-Path $PROFILE)/foo.txt"或)或表达式中的字符串串联(Write-Output ('One' + $var + '$Two')

[1]假设它们既不包含空格,也不包含任何PowerShell的元字符(请参阅this answer)。而引号通常采取的形式是将整个参数括在单引号或双引号中,视情况而定(例如'foo bar'"foo $var"),也可以用引号(转义)* 单个 * 字符(例如foo bar`),使用反引号(```),PowerShell的转义字符。

vyu0f0g1

vyu0f0g12#

关于about_Parsing help file

参数模式

解析时,PowerShell首先会将输入解释为表达式。但是当遇到命令调用时,解析将以参数模式继续。
参数模式设计用于分析shell环境中命令的参数和参数。除非使用以下语法之一,否则所有输入都被视为可扩展字符串
...
您可以将代码包含在子表达式($(...))中,以避免PowerShell将[int]$name1视为可扩展字符串,正如您已经发现的那样:-)

ax6ht2ek

ax6ht2ek3#

这是故意的。在将表达式传递给函数之前,需要用圆括号括起来,以便计算表达式。
如果你尝试在那里放置一个函数调用,例如:

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

返回为:

name1
-----
Write-Host

Write-Host [string]2返回[string]2而不是2

相关问题