更好地理解Powershell中的点表示法



为什么我可以这样做:

示例1

Get-NetIPConfiguration -Detailed | 
? IPv4DefaultGateWay -NE $null

示例2

Get-NetIPConfiguration -Detailed | 
? {$_.IPv4DefaultGateWay -NE $null -and $_.NetAdapter.Status -EQ "Up"}

但不是这个

示例3

Get-NetIPConfiguration -Detailed | 
? IPv4DefaultGateWay -NE $null -and NetAdapter.Status -EQ "Up"

我得到的错误消息如下:

Where-Object : Parameter set cannot be resolved using the specified named parameters.
At line:2 char:3
+   ? IPv4DefaultGateWay -NE $null -and NetAdapter.Status -EQ "Up"
+   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Where-Object], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,Microsoft.PowerShell.Commands.WhereObjectCommand

但很明显,Where-Object cmdlet能够通过使用-和-或来处理复合条件,因此错误消息令人困惑。


还有,为什么我可以做这个

示例4

Get-NetIPConfiguration -Detailed | 
? {$_.IPv4DefaultGateWay -NE $null -and $_.NetAdapter.Status -EQ "Up"} |
% {
New-Object -TypeName PSObject -Property @{
ComputerName = $_.ComputerName
IPAddress = $_.IPv4Address.IPv4Address
}
}

但无法进行

示例5

Get-NetIPConfiguration -Detailed | 
? {$_.IPv4DefaultGateWay -NE $null -and $_.NetAdapter.Status -EQ "Up"} |
Select-Object ComputerName, IPv4Address.IPv4Address

我的目标是通过使用示例4来实现,但这不是我在这里真正要求的——我在这里更关心的是寻址属性和属性的属性的约定!

这些差异最终源于PowerShell的两种基本解析模式,在这个答案中进行了解释;简而言之:

  • 参数模式类似于shell,用于调用带有空格分隔的自变量命令,并支持未加引号的字符串。

  • 表达式模式与传统编程语言类似,具有带引号的字符串、运算符、循环构造。。。

在您的两个示例中,命令都被调用(更具体地说,cmdlets,即Where-Object(其内置别名为?where(和Select-Object(其内置昵称为select(,因此传递给它们的内容将在参数模式中解析:

  • Where-Object IPv4DefaultGateWay -NE $null简化语法使用参数模式,即替代表达式模式脚本块({ ... }(的较低仪式参数模式,也可与ForEach-Objectcmdlet一起使用。

    • 语法更简单:

      • { ... }中无需外壳
      • 不需要通过自动$_变量引用手头的输入对象,只需使用属性名称本身就足够了
    • 但它是有限的:

      • 您只能在单个属性上执行单一操作[1]
      • 该属性必须是立即属性(例如,IPv4DefaultGateWay,而不是嵌套的属性(例如NetAdapter.Status(-详细信息如下
  • Select-Object也有一个约束,即给定的属性名称必须是立即属性。

    • 解决这一问题的唯一方法是通过计算属性,通过哈希表实现,该哈希表的Expression条目包含一个脚本块,用于计算每个输入对象的属性值-请参阅此答案

为什么在参数解析模式中不支持嵌套属性访问(例如NetAdapter.Status(

参数(如NetAdapter.Status,无论是否引用(作为字符串传递给命令,Where-ObjectSelect-Object将传递给其-Property参数的字符串解释为属性的逐字名称,而不是嵌套的属性访问表达式

也就是说,等效于以下命令,其中NetAdapter.Status参数模式中解析:

Get-NetIPConfiguration -Detailed |
Select-Object -ExpandProperty NetAdapter.Status

是以下表达式模式属性访问:

Get-NetIPConfiguration -Detailed |
ForEach-Object { $_.'NetAdapter.Status' }

请注意NetAdapter.Status周围的'...',显示该名称被逐字逐句地用作输入对象上的单个直接属性名称(不能按预期工作(。


设计原理

挑战在于,在自变量模式中,NetAdapter.Status'NetAdapter.Status'"NetAdapter.Status"之间没有区别——目标命令在所有情况下都能看到逐字的NetAdapter.Status,因此,与表达式不同的是,原始引用/非引用不能区分这些自变量形式。

然而,可以说,对于专门接受属性名称(参数-Property(的cmdlet来说,将参数(如NetAdapter.Status(解释为嵌套的属性访问器要有用得多,因为嵌入了.字符的属性名称。是罕见的。

改变这种行为将是一个打破的改变,然而,考虑到以下目前有效,但不再有效:

PS> '{ "foo.bar": 42 }' | ConvertFrom-Json | Select-Object foo.bar
foo.bar
-------
42

[1]这两种解析模式非常不同,不可能在自变量模式下重新创建所有表达式模式特征;您无法用命令参数(参数定义(对表达式模式的复杂性进行建模。简化语法是一种折衷方案,旨在使简单但常见的用例在语法上更容易

最新更新