【问题标题】:Can you combine CmdletBinding with unbound parameters?您可以将 CmdletBinding 与未绑定的参数结合使用吗?
【发布时间】:2016-10-09 03:08:16
【问题描述】:

(Powershell 5)
我有以下合并功能:

更新:删除了 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;

您可能会注意到我将决策逻辑封装在 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 测试和 unbound 参数(以及像 -Verbose 这样的通用参数,如果可能的话)。这样我也可以有一个用于 string 合并的通用算法:

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

我错过了什么吗?

【问题讨论】:

  • 顺便说一句,continue 是循环控制指令。很可能,当您将函数放入脚本时,它的行为会出乎您的意料。
  • @PetSerAl:你是对的。在与原始文件混在一起时,我添加了一些其他步骤——就像没有在 begin 块中指定 ScriptBlock 一样。我会改回来的。

标签: powershell parameter-passing


【解决方案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;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-21
    • 2014-03-10
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    相关资源
    最近更新 更多