powershell 方法.GetType()为同一变量返回不同的值

vc9ivgsu  于 2023-03-12  发布在  Shell
关注(0)|答案(2)|浏览(164)

在练习. GetType()方法时,我发现如果我编写一个返回“Name”和“BaseType”的函数,那么当我不编写该函数时,得到的结果与预期的结果不同。
示例:

function Get-BaseType_Name {
    param(
        [Parameter(Mandatory,ValuefromPipeline=$True)]
        [System.Object]$var   
    )
    $var.GetType() | Select -Property Name, BaseType;
} 
$var = Get-Service
$var | Get-BaseType_Name

我得到:

Name              BaseType                       
----              --------                       
ServiceController System.ComponentModel.Component

然而,如果我执行这些句子:

$var = Get-Service
$var.GetType() | Select -Property Name, BaseType;

我得到:

Name              BaseType                       
----              --------                       
Object[]          System.Array

为什么会这样?
我以为两种情况下会有相同的结果

ut6juiuv

ut6juiuv1#

根据@MatthiasRJessen的回答,您是通过管道一次一个地将服务对象传递到函数中。
不过,还有一点--你只看到一个输出结果的原因是因为你的函数等价于:

function Get-BaseType_Name {
    param(
        [Parameter(Mandatory,ValuefromPipeline=$True)]
        [System.Object]$var
    )
    END {
    #^^^^ END BLOCK
        $var.GetType() | Select -Property Name, BaseType;
    }
}

也就是说,您的函数体被视为end块,如果查看PowerShell为您的代码生成的AST,您可以确认这一点:

${function:Get-BaseType_Name}.Ast.Body

其产生以下输出。
(Note BeginBlockProcessBlock为空,并且您的代码出现在EndBlock中)。

Attributes         : {}
UsingStatements    : {}
ParamBlock         : param(
                             [Parameter(Mandatory,ValuefromPipeline=$True)]
                             [System.Object]$var
                         )
BeginBlock         :
^^^^^^^^^^ no definition

ProcessBlock       :
^^^^^^^^^^^^ no definition

EndBlock           : param(
                             [Parameter(Mandatory,ValuefromPipeline=$True)]
                             [System.Object]$var
                         )
                         $var.GetType() | Select -Property Name, BaseType
^^^^^^^^ your code is in this block

DynamicParamBlock  :
ScriptRequirements :
Extent             : {
                         param(
                             [Parameter(Mandatory,ValuefromPipeline=$True)]
                             [System.Object]$var
                         )
                         $var.GetType() | Select -Property Name, BaseType;
                     }
Parent             : function Get-BaseType_Name {
                         param(
                             [Parameter(Mandatory,ValuefromPipeline=$True)]
                             [System.Object]$var
                         )
                         $var.GetType() | Select -Property Name, BaseType;
                     }

如果您看一下about_Functions_Advanced_Methods中的end部分,它会说:

结束

此块用于为函数提供可选的一次性后处理。
PowerShell将Get-Service的每个结果一次一个地输入到Get-BaseType_Name函数中,但代码只有在处理完 last 项后才真正执行任何操作,在这种情况下,$var保存了对最后一项的引用,如果运行以下代码,您可以看到这一点:

function Get-BaseType_Name {
    param(
        [Parameter(Mandatory,ValuefromPipeline=$True)]
        [System.Object]$var
    )
    write-host $var.Name
    #^^^^^^^^^^^^^^^^^^^ -- write the name of the service to the console
    $var.GetType() | Select -Property Name, BaseType;
}

Get-Service | Get-BaseType_Name

在我的计算机上,我看到以下输出:

XboxNetApiSvc

Name              BaseType
----              --------
ServiceController System.ComponentModel.Component

其中XboxNetApiSvcGet-Service返回的最后一个服务的名称。
如果你想看到每个项的输出,你可以把你的代码放在process块中:

function Get-BaseType_Name {
    param(
        [Parameter(Mandatory,ValuefromPipeline=$True)]
        [System.Object]$var
    )
    PROCESS {
    #^^^^^^^^ PROCESS BLOCK
        $var.GetType() | Select -Property Name, BaseType;
    }
}

Get-Service | Get-BaseType_Name

然后你的optput看起来像这样

Name              BaseType
----              --------
ServiceController System.ComponentModel.Component
ServiceController System.ComponentModel.Component
ServiceController System.ComponentModel.Component
... etc ...

总之,您正在通过管道发送一个服务集合(根据@MathiasRJessen的回答),但您只返回了该集合中最后一个项的值,因为PowerShell隐式地将您的函数体视为end块...

dgiusagp

dgiusagp2#

about_Pipelines帮助主题中:

一次处理一个

[...]
当你通过管道将多个对象发送到命令时,PowerShell会将这些对象一次发送一个给命令。当你使用命令参数时,这些对象将作为单个数组对象发送。这个微小的差异会产生重大的影响。
执行管道时,PowerShell自动枚举实现IEnumerable接口的任何类型,并通过管道一次发送一个成员。[hashtable]除外,它需要调用GetEnumerator()方法。
换句话说:这是设计使然- PowerShell发现$var包含一个数组(实现IEnumerable接口的类型),并开始逐一枚举其中的项目
您可以使用Write-Output -NoEnumerate来阻止自动枚举:

PS ~> Write-Output $var -NoEnumerate | Get-BaseType_Name

Name              BaseType                       
----              --------                       
Object[]          System.Array

相关问题