尝试编写接受流水线输入的 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
我试图了解 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