none
パイプラインを使用したパラメータセットが解決できない RRS feed

  • 質問

  • ある関数でCsvパラメータをパイプとパラメータ指定双方に対応しようとしているのですが、
    パラメータセットの解決ができません。どのようにすれば解決できるでしょうか。
    PSVersionは5.1.18362.752です。

    まず、以下のように関数を定義しました。

    function test
    {
            [CmdletBinding(DefaultParameterSetName='InputPath')]
            Param(
    
                [Parameter(Mandatory,ParameterSetName= "InputPath",Position=0)]
                [string]$InputPath,
    
                [Parameter(Mandatory,ParameterSetName= "CSV",Position=0)]
                [Parameter(Mandatory,ValueFromPipeLine,ParameterSetName="Pipe")]
                $Csv,
    
                [Parameter(Mandatory,ParameterSetName= "CSV",Position=1)]
                [Parameter(Mandatory,ParameterSetName= "InputPath",Position=1)]
                [Parameter(Mandatory,ValueFromPipeLine,ParameterSetName="Pipe",Position=0)]
                [string]$OutPath
            )
    
        "Parameterset : $($PSCmdlet.ParameterSetName)"
        foreach($k in $PSBoundParameters.keys)
    	{
           "$k = $($PSBoundParameters[$k])"
        }
    }
    

    その後、構文を確認したところ、およそ意図したものに見えます。

    PS C:\> get-command test -syntax
    test [-InputPath] <string> [-OutPath] <string> [<CommonParameters>]
    test [-OutPath] <string> -Csv <Object> [<CommonParameters>]
    test [-Csv] <Object> [-OutPath] <string> [<CommonParameters>]
    

    しかし、パイプもInputPathパラメータも機能しますが、CSVパラメータだけ機能しません。

    PS C:\>$c = get-childitem
    PS C:\>$csv | test -outpath "outpath"
    Parameterset : Pipe
    OutPath = outpath
    Csv = Videos
    
    PS C:\>test -inputpath "csvpath" -outpath "outpath"
    Parameterset : InputPath
    InputPath = csvpath
    OutPath = outpath
    
    PS C:\> test -csv $csv -outpath "outpath"
    test : 指定された名前のパラメーターを使用してパラメーター セットを解決できません。
    発生場所 行:1 文字:1
    + test -csv $csv -outpath "outpath"
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [test]、ParameterBindingException
        + FullyQualifiedErrorId : AmbiguousParameterSet,test
    


    2020年6月4日 7:07

回答

  • 標準コマンドレットのsyntaxを見ていると、パイプライン入力可能なパラメータAと、パラメータ名省略可能なパラメータBの両方がある場合、$a|command $bを実行可能にするためには、BをPosition=0にする必要があるみたいですね。

    これに従えば、$OutPathをPosition=0に、$InputPathと$CsvをPosition=1にするしかないのかもしれません。

    パラメータ名省略時の-InputPathと-OutPathの順序が元と逆になってしまいますが。

    (以下、追記)

    もっとも、InputとOutputの指定順が逆になるのは非常に直感に反するので、実際にはナシでしょう。パイプライン入力時には-Outputのパラメータ名必須、としたほうがマシに思います。

    同じようなジレンマを抱えた標準コマンドレットに、Join-Pathがあります。

    Join-Path "C:\test" "child"

    はできますが、

    "C:\test"|Join-Path "child"

    はできず、

    "C:\test"|Join-Path -ChildPath "child"

    とする必要があります。


    2020年6月15日 19:33
    モデレータ

すべての返信

  • CsvパラメータセットとPipeパラメータセットで、含まれるパラメータが属性を除いてすべて同じ([string]$OutPathと[object]$Csv)になってしまっています。それがパラメータセットが解決できない理由だと思われます。

    ValueFromPipeLine属性は、「パイプラインから値を受け取るパラメータ」ではなく、「パイプラインから値を受け取ることもできるパラメータ」にする属性なので、パラメータセットを分ける必要はないです。

    あと、$OutPathにもValueFromPipeLine属性が付いていますが、$csv | test と実行できてしまい、$OutPathにパイプラインの値が渡ってしまうので、消去する必要があります。

    お望みの挙動をさせるには、おそらく以下のようなパラメータ定義になるかと思います。

            [CmdletBinding(DefaultParameterSetName='InputPath')]
            Param(
    
                [Parameter(Mandatory,ParameterSetName= "InputPath",Position=0)]
                [string]$InputPath,
    
                [Parameter(Mandatory,ValueFromPipeLine,ParameterSetName= "CSV",Position=0)]
                $Csv,
    
                [Parameter(Mandatory,ParameterSetName= "CSV",Position=1)]
                [Parameter(Mandatory,ParameterSetName= "InputPath",Position=1)]
                [string]$OutPath
            )

    2020年6月4日 21:41
    モデレータ
  • 牟田口大介様
    ありがとうございます。パラメータセットをValueFromPipeLineで分けているのは、

    $csv | test "outpath"

    のようにパイプで渡した際に OutPathパラメータをPosition0で認識できるようにする意図があります。

    いただいたパラメータセットですと、

    PS C:\>
    PS C:\> get-command test -syntax
    test [-InputPath] <string> [-OutPath] <string> [<CommonParameters>]
    test [-Csv] <Object> -OutPath <string> [<CommonParameters>]
    
    PS C:\> $csv = get-childitem
    PS C:\> $csv | test -outpath "outpath"
    Parameterset : CSV
    OutPath = outpath
    Csv = Videos
    
    PS C:\> test -inputpath "csvpath" -outpath "outpath"
    Parameterset : InputPath
    InputPath = csvpath
    OutPath = outpath
    
    PS C:\> test -csv $csv -outpath "outpath"
    Parameterset : CSV
    Csv = .vscode 3D Objects Contacts Desktop Documents Downloads Favorites Links Music OneDrive Pictures Saved Games Searches Videos
    OutPath = outpath
    
    PS C:\> $csv | test "outpath"
    コマンド パイプライン位置 1 のコマンドレット test
    次のパラメーターに値を指定してください:
    OutPath:

    このようにOutPathが認識されませんでした。

    2020年6月5日 0:44
  • とはいえPipelineを分けても意図した動きにならないですね。。。Pipeで渡しているのでパラメータセットはPipeが確定するはずと思ったのですが

    > ValueFromPipeLine属性は、「パイプラインから値を受け取るパラメータ」ではなく、「パイプラインから値を受け取ることもできるパラメータ」にする


    ということでパラメータセットの解決のパターンには含まれないのか…。いただいた情報をもとにOutPathからValueFromPipeLine属性を削除して、Csvをパイプ有り無しで分けるパターンを作ってみましたが、こちらでもOutPathが聞かれてしまいました。

    Syntax上では「test [-OutPath] <string> [-Csv <Object>] [<CommonParameters>]」があるのでこれが採用されそうなものですが…。

    PS C:\> function test
    >> {
    >>  [CmdletBinding(DefaultParameterSetName='InputPath')]
    >>         [CmdletBinding(DefaultParameterSetName='InputPath')]
    >>         Param(
    >>             [Parameter(Mandatory,ParameterSetName= "InputPath",Position=0)]
    >>             [string]$InputPath,
    >>
    >>             [Parameter(ValueFromPipeLine,ParameterSetName="Pipe")]
    >>             [Parameter(Mandatory,ParameterSetName= "CSV",Position=0)]
    >>             $Csv,
    >>
    >>             [Parameter(Mandatory,ParameterSetName= "CSV",Position=1)]
    >>             [Parameter(Mandatory,ParameterSetName= "InputPath",Position=1)]
    >>             [Parameter(Mandatory,ParameterSetName="Pipe",Position=0)]
    >>             [string]$OutPath
    >> )
    >>
    >>     "Parameterset : $($PSCmdlet.ParameterSetName)"
    >>     foreach($k in $PSBoundParameters.keys)
    >> {
    >>        "$k = $($PSBoundParameters[$k])"
    >>     }
    >> }
    PS C:\> get-command test -syntax
    test [-InputPath] <string> [-OutPath] <string> [<CommonParameters>]
    
    test [-Csv] <Object> [-OutPath] <string> [<CommonParameters>]
    
    test [-OutPath] <string> [-Csv <Object>] [<CommonParameters>]
    
    PS C:\> $csv = get-childitem | select -first 3
    PS C:\> $csv | test -outpath "outpath"
    Parameterset : Pipe
    OutPath = outpath
    Csv = Program Files (x86)
    PS C:\> $csv | test "outpath"
    コマンド パイプライン位置 1 のコマンドレット test
    次のパラメーターに値を指定してください:
    OutPath:
    PS C:\> test -csv $csv -outpath "outpath"
    test : 指定された名前のパラメーターを使用してパラメーター セットを解決できません。
    発生場所 行:1 文字:1
    + test -csv $csv -outpath "outpath"
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [test]、ParameterBindingException
        + FullyQualifiedErrorId : AmbiguousParameterSet,test
    
    PS C:\> test -csv $csv "outpath"
    test : 指定された名前のパラメーターを使用してパラメーター セットを解決できません。
    発生場所 行:1 文字:1
    + test -csv $csv "outpath"
    + ~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [test]、ParameterBindingException
        + FullyQualifiedErrorId : AmbiguousParameterSet,test
    
    PS C:\> test -inputpath "inputpath" "outpath"
    Parameterset : InputPath
    InputPath = inputpath
    OutPath = outpath
    PS C:\> test "inputpath" "outputpath"
    Parameterset : InputPath
    InputPath = inputpath
    OutPath = outputpath
    • 編集済み やき(Yaki) 2020年6月5日 1:14 改行を調整
    2020年6月5日 1:07
  • 標準コマンドレットのsyntaxを見ていると、パイプライン入力可能なパラメータAと、パラメータ名省略可能なパラメータBの両方がある場合、$a|command $bを実行可能にするためには、BをPosition=0にする必要があるみたいですね。

    これに従えば、$OutPathをPosition=0に、$InputPathと$CsvをPosition=1にするしかないのかもしれません。

    パラメータ名省略時の-InputPathと-OutPathの順序が元と逆になってしまいますが。

    (以下、追記)

    もっとも、InputとOutputの指定順が逆になるのは非常に直感に反するので、実際にはナシでしょう。パイプライン入力時には-Outputのパラメータ名必須、としたほうがマシに思います。

    同じようなジレンマを抱えた標準コマンドレットに、Join-Pathがあります。

    Join-Path "C:\test" "child"

    はできますが、

    "C:\test"|Join-Path "child"

    はできず、

    "C:\test"|Join-Path -ChildPath "child"

    とする必要があります。


    2020年6月15日 19:33
    モデレータ
  • 牟田口大介様

    おお!なるほどそのような動きが…。

    そうなるとこれはもう如何ともしがたい仕様のようですね。

    ありがとうございます。

    2020年6月18日 9:30