2016-06-08 85 views
2

(PowerShell的5)
我有以下COALESCE函数:您可以将CmdletBinding与未绑定参数结合使用吗?

更新:删除在process块中的 “优化” continue调用)

function Find-Defined { 
    begin { 
     $ans = $NULL; 
     $Test = { $_ -ne $NULL }; 
    } 
    process { 
     if ($ans -eq $NULL) { 
      $ans = $_ |? $Test | Select -First 1; 
     } 
    } 
    end  { 
     if ($ans -ne $NULL) { 
      return $ans; 
     } 
     else { 
      $Args ` 
       |% { if ($_ -is [Array]) { $_ |% { $_ } } else { $_ } } ` 
       |? $Test ` 
       | Select -First 1 ` 
       | Write-Output ` 
       ; 
     } 
    } 
} 

而且这个作品足够好了我在命令行如下:

$NULL, $NULL, 'Legit', 1, 4 | Find-Defined; 

$NULL, $NULL | Find-Defined $NULL, @($NULL, 'Value'), 3; 

$NULL, $NULL | Find-Defined $NULL $NULL 3 4; 

您可能会注意到t我把决策逻辑封装在一个ScriptBlock变量中。这是因为我想参数化,我开始尝试这个。

[CmdletBinding()]param([ScriptBlock] $Test = { $_ -ne $NULL }); 

但是,我加了一分钟CmdletBinding我开始出现错误。该绑定想尝试在参数部分为ScriptBlock投了一切,所以我加了

[CmdletBinding(PositionalBinding=$False)] 

然后它抱怨说,未绑定的参数不能绑定的,所以我说:

param([parameter(Mandatory=$False,Position=0,ValueFromRemainingArguments=$True)][Object[]] $Arguments ... 

而且我之后做的任何事情都添加了新的错误。如果我删除了$Test参数,只是它本地化看看我能做些什么,然后我就开始收到错误开发的第一代,当我有:

The input object cannot be bound to any parameters for the command either 
because the command does not take pipeline input or the input and its 
properties do not match any of the parameters that take pipeline input. 

...即使我有一个process块。

最后,简单地删除param声明,把它放回到我喜欢的灵活函数中。

我还是想扩大这个功能同时接受一个ScriptBlock测试和绑定参数(以及常见的参数,如-Verbose,如果可能的话)。这样我可以有合并以及一般的算法:

$Test = { -not [string]::IsNullOrEmpty([string]$_) }; 
$NULL,$NULL,'','' | Find-Defined -Test $Test $NULL,'','This should be it' 'Not seen' 

我缺少的东西?

+1

顺便提一下,'continue'是循环控制指令。很可能,当你将你的函数放在脚本中时,它对你来说意想不到。 – PetSerAl

+0

@PetSerAl:你说得对。在原版中,我添加了一些其他步骤 - 就像没有在'begin'块中指定的'ScriptBlock'一样。我会改回它。 – Axeman

回答

1

我相信这解决了您正在尝试解决的问题,但有一些不同的实现。当使用CmdletBinding时,一切都必须声明。因此,您需要一个参数用于管道输入,另一个用于“未绑定”参数。

基于你的问题我写这些测试用例:

Describe 'Find-Defined' { 
    it 'should retun Legit' { 
    $NULL, $NULL, 'Legit', 1, 4 | Find-Defined | should be 'Legit' 
    } 
    it 'should retun Value' { 
    $NULL, $NULL | Find-Defined $NULL, @($NULL, 'Value'), 3 | should be 'Value' 
    } 
    it 'should retun 3' { 
    $NULL, $NULL | Find-Defined $NULL $NULL 3 4 | should be '3' 
    } 
    it 'Should return "This should be it"' { 
    $Test = { -not [string]::IsNullOrEmpty([string]$_) }; 
    $NULL,$NULL,'','' | Find-Defined -Test $Test $NULL,'','This should be it' 'Not seen' | should be 'This should be it' 
    } 
} 

这里是我的解决方案,它通过了所有上述情况。

function Find-Defined { 
    [CmdletBinding()] 
    param (
    [ScriptBlock] $Test = { $NULL -ne $_}, 
    [parameter(Mandatory=$False,ValueFromPipeline =$true)] 
    [Object[]] $InputObject, 
    [parameter(Mandatory=$False,Position=0,ValueFromRemainingArguments=$True)] 
    [Object[]] $Arguments 
) 

    begin { 
     $ans = $NULL; 

     function Get-Value { 
      [CmdletBinding()] 
      param (
      [ScriptBlock] $Test = { $_ -ne $NULL }, 
      [parameter(Mandatory=$False,Position=0,ValueFromRemainingArguments=$True)] 
      [Object[]] $Arguments, 
      $ans = $NULL 
     ) 
      $returnValue = $ans 
      if($null -eq $returnValue) 
      { 
      foreach($Argument in $Arguments) 
      { 
       if($Argument -is [object[]]) 
       { 
       $returnValue = Get-Value -Test $Test -Arguments $Argument -ans $returnValue 
       } 
       else 
       { 
       if ($returnValue -eq $NULL) { 
        $returnValue = $Argument |Where-Object $Test | Select-Object -First 1; 
        if($null -ne $returnValue) 
        { 
         return $returnValue 
        } 
       } 
       } 
      } 
      } 
      return $returnValue 
     } 
    } 
    process { 
     $ans = Get-Value -Test $Test -Arguments $InputObject -ans $ans 
    } 
    end  { 
     $ans = Get-Value -Test $Test -Arguments $Arguments -ans $ans 

     if ($ans -ne $NULL) { 
      return $ans; 
     } 
    } 
} 
相关问题