尝试编写接受流水线输入的 powershell cmdlet

trying to write powershell cmdlet accepting pipelined input

我试图了解 powershell 并编写一个函数作为 cmdlet,在其中一篇文章中找到了以下代码示例,但它似乎不想作为 cmdlet 工作,即使它有 [cmdletbinding()] 文件顶部的声明。

当我尝试做类似

的事情时
1,2,3,4,5 | .\measure-data 

它 returns 空响应(如果我在文件底部和 运行 文件本身调用它,函数本身工作得很好)。

这是我正在使用的代码,我们将不胜感激:)

Function Measure-Data {
    <#
    .Synopsis
    Calculate the median and range from a collection of numbers
    .Description
    This command takes a collection of numeric values and calculates the
    median and range. The result is written as an object to the pipeline.
    .Example
    PS C:\> 1,4,7,2 | measure-data

    Median                                    Range
    ------                                    -----
    3                                        6

    .Example
    PS C:\> dir c:\scripts\*.ps1 | select -expand Length | measure-data

    Median                                    Range
    ------                                    -----
    1843                                   178435
    #>

    [cmdletbinding()]
    Param (
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [ValidateRange([int64]::MinValue,[int64]::MaxValue)]
        [psobject]$InputObject 
    )
    
    Begin {
        #define an array to hold incoming data
        Write-Verbose "Defining data array"
        $Data=@()
    } #close Begin
    
    Process {
        #add each incoming value to the $data array
        Write-Verbose "Adding $inputobject"
        $Data+=$InputObject
    } #close process
    
    End {
        #take incoming data and sort it
        Write-Verbose "Sorting data"
        $sorted = $data | Sort-Object
    
        #count how many elements in the array
        $count = $data.Count
        Write-Verbose "Counted $count elements"
        
        #region calculate median
    
        if ($sorted.count%2) {
            <#
            if the number of elements is odd, add one to the count
            and divide by to get middle number. But arrays start
            counting at 0 so subtract one
            #>
            Write-Verbose "processing odd number"            
            [int]$i = (($sorted.count+1)/2-1)            
            #get the corresponding element from the sorted array
            $median = $sorted[$i]    
        }
        else {
            <#
            if number of elements is even, find the average
            of the two middle numbers
            #>
            Write-Verbose "processing even number"
            $i = $sorted.count/2
            #get the lower number
            $x = $sorted[$i-1]
            #get the upper number
            $y = $sorted[-$i]
            #average the two numbers to calculate the median
            $median = ($x+$y)/2
        } #else even
    
        #endregion
    
        #region calculate range

        Write-Verbose "Calculating the range"
        $range = $sorted[-1] - $sorted[0]
    
        #endregion
    
        #region write result

        Write-Verbose "Median = $median"
        Write-Verbose "Range = $range"
        #define a hash table for the custom object
        $hash = @{Median=$median;Range=$Range}
    
        #write result object to pipeline
        Write-Verbose "Writing result to the pipeline"
        New-Object -TypeName PSobject -Property $hash
    
        #endregion
    } #close end
} #close measure-data

这是我从中获取代码的文章: https://mcpmag.com/articles/2013/10/15/blacksmith-part-4.aspx

编辑:也许我应该添加本文前面部分的这段代码的版本工作得很好,但是在添加了所有使它成为合适的 cmdlet 的东西之后,比如帮助部分和冗长的行,这个东西就没有了想工作,我相信缺少一些东西,我觉得这可能是因为它是为 powershell 3 编写的,我正在 win 10 ps 5 点上测试它,但老实说我不甚至知道我应该找哪个方向,所以我才向你求助

您传递给它的不是一个对象,而是一个整数数组。如果将参数更改为:

    Param (
        [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
        [ValidateRange([int64]::MinValue,[int64]::MaxValue)]
        [Int[]]$InputObject 
    )

现在一切正常:

PS> 1,2,3,4,5 | Measure-Data

Median Range
------ -----
     3     4

代码没有任何问题(除了可能的优化),但是你调用它的方式不能工作:

1,2,3,4,5 | .\measure-data

当您调用包含命名函数的脚本文件时,预计“什么也不会发生”。实际上,脚本在运行,但 PowerShell 不知道它应该调用哪个函数(可能有多个)。所以它只运行函数之外的任何代码。

您有两种解决问题的方法:

选项 1

去掉function关键字和属于它的花括号。保留 [cmdletbinding()]Param 部分。

[cmdletbinding()]
Param (
    [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
    [ValidateRange([int64]::MinValue,[int64]::MaxValue)]
    [psobject]$InputObject 
)

Begin {
    # ... your code ...
} #close Begin

Process {
    # ... your code ...
} #close process

End {
    # ... your code ...
}

现在脚本本身就是“函数”,可以这样调用:

1,2,3,4,5 | .\measure-data

选项 2

将脚本变成模块。基本上你只需要用 .psm1 扩展名来保存它(there is more to it,但为了开始就足够了)。

在您想要使用函数的脚本中,您必须先导入模块,然后才能使用它的函数。如果没有安装该模块,您可以通过指定其完整路径来导入它。

# Import module from directory where current script is located
Import-Module $PSScriptRoot\measure-data.psm1

# Call a function of the module
1,2,3,4,5 | Measure-Data

当单个脚本文件中有多个函数时,模块就是一种方式。当一个函数被多次调用时它也更有效,因为 PowerShell 只需要解析它一次(它会记住 Import-Module 次调用)。

它按原样工作,你只需要正确调用它。由于代码现在是一个函数,当代码直接在文件中时,您不能像以前那样调用它

# method when code is directly in file with no Function Measure-Data {}
1,2,3,4,5 | .\measure-data 

现在您已经定义了函数,您需要对文件进行点源,以便它将您的函数加载到内存中。然后你可以通过它的名字调用你的函数(恰好与文件名相同,但不一定是)

# Load the functions by dot-sourcing
. .\measure-data.ps1

# Use the function
1,2,3,4,5 | Measure-Data