问题描述
PS 版本:5.1
希望
我希望能奏效$obj = [PSCustomObject]@{
PSTypeName = 'MyObject'
con = 'local'
}
add-member -MemberType ScriptMethod -InputObject $obj -Name MyMethod -Value {
param([parameter(ValueFromPipeline=$true)]$x)
begin {write-host 'begin'}
process{write-host "$($this.con): $x"}
end {write-host 'end'}
}
1..5 | $obj.MyMethod
给予
begin
local: 1
local: 2
local: 3
local: 4
local: 5
end
但是报错
At line:1 char:8
+ 1..5 | $obj.MyMethod
+ ~~~~~~~~~~~~~
Expressions are only allowed as the first element of a pipeline.
+ CategoryInfo : ParserError: (:) [],ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ExpressionsMustBeFirstInPipeline
试过
什么不起作用,因为:idk,tl;dr 原因?1..5 | %{$obj.MyMethod($_)}
什么不起作用,因为:开始/结束
$obj = [PSCustomObject]@{
PSTypeName = 'MyObject'
con = 'local'
}
add-member -MemberType ScriptMethod -InputObject $obj -Name MyMethod -Value {
param($x)
write-host "$($this.con): $x"
}
1..5 | %{$obj.MyMethod($_)}
什么不起作用,因为:$this 不再附加
$obj = [PSCustomObject]@{
PSTypeName = 'MyObject'
con = 'local'
MyMethodSB = {
param()
begin {write-host 'begin'}
process{write-host "$($obj.con): $_"}
end {write-host 'end'}
}
}
1..5 | &$obj.MyMethodSB
什么不起作用,因为:idk,也许 Stepper 的范围过早消亡
$obj = [PSCustomObject]@{
PSTypeName = 'MyObject'
con = 'local'
Stepper = $null
}
add-member -MemberType ScriptMethod -InputObject $obj -Name MyMethod -Value {
param($x)
if($null -eq $this.Stepper){
$t = $this
$sb = {
param($o)
begin {write-host 'begin'}
process{write-host "$($o.con): $_"}
end {write-host 'end'}
}
$this.Stepper = {&$sb $t}.GetSteppablePipeline()
$this.Stepper.Begin($true)
}
$this.Stepper.Process($x)
}
1..5 | %{$obj.MyMethod($_)}
$obj.Stepper.End()
$obj.Stepper = $null
差不多了
什么是有效的$Data = @{
ItemNo1 = @(
@{loc = 'WH'; qty = 20}
@{loc = 'DK'; qty = 0}
@{loc = 'ST1'; qty = 3}
@{loc = 'ST2'; qty = 2}
)
ItemNo2 = @(
@{loc = 'WH'; qty = 6}
@{loc = 'DK'; qty = 0}
)
ItemNo3 = @(
@{loc = 'WH'; qty = 100}
@{loc = 'ST1'; qty = 5}
@{loc = 'DK'; qty = 0}
)
ItemNo4 = @(
@{loc = 'WH'; qty = 0}
@{loc = 'DK'; qty = 0}
@{loc = 'ST2'; qty = 15}
)
ItemNo5 = @(
@{loc = 'WH'; qty = 0}
@{loc = 'DK'; qty = 15}
)
}
$QueryParameter = [PSCustomObject]@{Value = $null}
function MockDataQuery{
$Item = $QueryParameter.Value
foreach($iDat in $Data.$Item){
" $Item;$($iDat.loc);$($iDat.qty)"
}
}
$InvObj = [PSCustomObject]@{
PSTypeName = 'Data.Connection'
con = 'item.inventory'
}
add-member -MemberType ScriptMethod -InputObject $InvObj -Name FetchItemData -Value {
$p = $this
$qPipe = [PSCustomObject]@{
PSTypeName = 'Data.Connection.QueryPipe'
parent = $p
qHeader = 'Item;Location;Quantity'
query = 'select * from inventory where item = ?'
param = $null
}
add-member -MemberType ScriptMethod -InputObject $qPipe -Name begin -Value {
param([bool]$Header=$true)
write-host "connect to: $($this.parent.con)"
write-host "compile command: $($this.query)"
$this.param = $QueryParameter
write-host 'begin transaction'
if($Header){
write-host " $($this.qHeader)"
}
}
add-member -MemberType ScriptMethod -InputObject $qPipe -Name process -Value {
param($itm)
$this.param.Value = $itm
MockDataQuery | write-host
}
add-member -MemberType ScriptMethod -InputObject $qPipe -Name end -Value {
write-host 'end transaction'
write-host 'dispose command'
write-host 'close connection'
}
return $qPipe
}
1..5 | %{"ItemNo$_"} | &{
begin {$stp = $InvObj.FetchItemData();$stp.begin()}
process{$stp.process($_)}
end {$stp.end()}
}
虽然这似乎是一个功能模型,但如果有一种解决方案不需要连接对象的用户每次使用时都必须在他们的末尾编写开始/处理/结束脚本,那就更好了对象的方法。
解决方法
几天后喝了太多咖啡;我想我在这里学到的是,如果可以,请尝试重构为一个很好的旧直接高级功能/cmdlet
$Data = @{
ItemNo1 = @(
@{loc = 'WH'; qty = 20}
@{loc = 'DK'; qty = 0}
@{loc = 'ST1'; qty = 3}
@{loc = 'ST2'; qty = 2}
)
ItemNo2 = @(
@{loc = 'WH'; qty = 6}
@{loc = 'DK'; qty = 0}
)
ItemNo3 = @(
@{loc = 'WH'; qty = 100}
@{loc = 'ST1'; qty = 5}
@{loc = 'DK'; qty = 0}
)
ItemNo4 = @(
@{loc = 'WH'; qty = 0}
@{loc = 'DK'; qty = 0}
@{loc = 'ST2'; qty = 15}
)
ItemNo5 = @(
@{loc = 'WH'; qty = 0}
@{loc = 'DK'; qty = 15}
)
}
$QueryParameter = [PSCustomObject]@{Value = $null}
function MockDataQuery{
$Item = $QueryParameter.Value
foreach($iDat in $Data.$Item){
" $Item;$($iDat.loc);$($iDat.qty)"
}
}
filter Write-PassThru{
write-host $_ -ForegroundColor Cyan
$_
}
$InvObj = [PSCustomObject]@{
PSTypeName = 'Data.Connection'
con = 'item.inventory'
FetchItemData = 'Get-ItemData'
}
function Get-ItemData{
param(
[parameter(mandatory=$true)]
[PSTypeName('Data.Connection')]
$t,[bool]
$Header=$true,[Parameter(ValueFromPipeline=$true)]
$itm
)
begin{
$query = 'select * from inventory where item = ?'
$qHeader = 'Item;Location;Quantity'
$param = $null
write-host "connect to: $($t.con)"
write-host "compile command: $query"
$param = $QueryParameter
write-host 'begin transaction'
if($Header){
" $qHeader" | write-passthru
}
}process{
$param.Value = $itm
MockDataQuery | write-passthru
}end{
write-host 'end transaction'
write-host 'dispose command'
write-host 'close connection'
}
}
1..5 `
| %{"ItemNo$_"} `
| &$InvObj.FetchItemData $InvObj
但是如果你不能,你也可以尝试做一些像这样非常hacky的事情
$Data = @{
ItemNo1 = @(
@{loc = 'WH'; qty = 20}
@{loc = 'DK'; qty = 0}
@{loc = 'ST1'; qty = 3}
@{loc = 'ST2'; qty = 2}
)
ItemNo2 = @(
@{loc = 'WH'; qty = 6}
@{loc = 'DK'; qty = 0}
)
ItemNo3 = @(
@{loc = 'WH'; qty = 100}
@{loc = 'ST1'; qty = 5}
@{loc = 'DK'; qty = 0}
)
ItemNo4 = @(
@{loc = 'WH'; qty = 0}
@{loc = 'DK'; qty = 0}
@{loc = 'ST2'; qty = 15}
)
ItemNo5 = @(
@{loc = 'WH'; qty = 0}
@{loc = 'DK'; qty = 15}
)
}
$QueryParameter = [PSCustomObject]@{Value = $null}
function MockDataQuery{
$Item = $QueryParameter.Value
foreach($iDat in $Data.$Item){
" $Item;$($iDat.loc);$($iDat.qty)"
}
}
filter Write-PassThru{
write-host $_ -ForegroundColor Cyan
$_
}
$InvObj = [PSCustomObject]@{
PSTypeName = 'Data.Connection'
con = 'item.inventory'
FetchItemData = {
param(
[bool]
$Header=$true,[Parameter(ValueFromPipeline=$true)]
$itm
)
begin{
$pm = $MyInvocation.PositionMessage -split "`n"
[int]$at = $pm[0] -replace '.*char:(\d+).*','$1'
$l = ($pm[2] -replace '[^~]','').Length
$myCall = $pm[1].substring($at+1,$l)
$thus = get-variable -Name ($myCall -replace '.*?\${?([^}.]+)}?\..*','$1') -ValueOnly
$query = 'select * from inventory where item = ?'
$qHeader = 'Item;Location;Quantity'
$param = $null
write-host "connect to: $($thus.con)"
write-host "compile command: $query"
$param = $QueryParameter
write-host 'begin transaction'
if($Header){
" $qHeader" | write-passthru
}
}process{
$param.Value = $itm
MockDataQuery | write-passthru
}end{
write-host 'end transaction'
write-host 'dispose command'
write-host 'close connection'
}
}
}
function Annotate-Flow{
param(
[string]
$tag = 'Flow',[switch]
$head,[switch]
$tail
)
begin{
if($head){
$Global:FlowSequence = 0
}else{$Global:FlowSequence++}
write-host ("${Tag} Seq[{0,2}]: Begin" -f $Global:FlowSequence) -ForegroundColor DarkCyan
}process{
$Global:FlowSequence++
write-host ("${Tag} Seq[{0,2}]: Process($_)" -f $Global:FlowSequence)-ForegroundColor DarkCyan
$_
}end{
$Global:FlowSequence++
write-host ("${Tag} Seq[{0,2}]: End" -f $Global:FlowSequence) -ForegroundColor DarkCyan
if($tail){
remove-variable -Scope Global -Name FlowSequence
}
}
}
1..5 | Annotate-Flow Source -head `
| %{"ItemNo$_"} | Annotate-Flow Format `
| &$InvObj.FetchItemData | Annotate-Flow Lookup -tail
您可能(不)想阅读的一些内容:
- MSDN: Cmdlet Overview
- MSDN: How Windows PowerShell Works
- Windows PowerShell Object Concepts
- Customizing Windows PowerShell - Part 2
- MSDN: How to write a cmdlet
- MSDN: Cmdlet Attribute Declaration
- MSDN: SteppablePipeline Class
- Add-Type.Tests.ps1
虽然说实话,这更像是一个凑合的答案,而不是一个优雅的解决方案。