none
Named Parameters not working as expected

    Question

  • Hi,
     
    Using PowerShell 2.0
     
    At start of script .\PsMyScript.ps1
     
    Param{
    [string]$p1,
    [string]$p2,
    [string]$p3
    }
     
    Call the script - works as expected.
     
    .\PsMyScript.ps1 -p2 "two" -p3 "three" -p1 "one"
     
    Call the script - NOT working as expected
     
    .\PsMyScript.ps1 "one" "two" "three"
     
    The problem is that the parameters are still being populated even though I
    didn't supply their names. I'd like to force the parameters to be NAMED
    parameters, but I don't need them to be mandatory.
     
     
    lundi 23 mai 2011 13:36

Réponses

  • You can do something like this in your script:

    param(
    $bad_param,
    [string]$p1,
    [string]$p2,
    [string]$p3
    )

    if ($bad_param){write-host "Bad parameter";exit}

    Any unnamed parameter will get picked up by $bad_param.


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    • Modifié mjolinor mardi 24 mai 2011 15:07
    • Proposé comme réponse jrich jeudi 26 mai 2011 12:53
    • Marqué comme réponse Alan ZhuModerator vendredi 27 mai 2011 03:25
    mardi 24 mai 2011 13:55

Toutes les réponses

  • Try using - Position.

    Param ( [parameter(Position=0)] [String[]] $p1 )

     

    get-help about_Functions_Advanced_Parameters

    lundi 23 mai 2011 13:59
  • "Kazun" wrote in message
    news:fc48f2fe-9e2a-4395-aff7-7954915439af@communitybridge.codeplex.com...
    > Try using - Position.
    >
    > Param ( [parameter(Position=0)] [String[]] $p1 )
    >
    > get-help about_Functions_Advanced_Parameters
     
    Thanks, I did read about_Functions_Advanced_Parameters, but didn't
    understand it.
    Are you saying to use [parameter(Position=0)] for all parameters? I thought
    it was forcing a positional parameter, which sounded like the opposite to
    what I wanted...
     
     
    lundi 23 mai 2011 14:50
  • No for all:
    Param(
    	[parameter(Position=0)]
    	[string]$p1,
    	[parameter(Position=1)]
    	[string]$p2,
    	[parameter(Position=2)]
    	[string]$p3
    	)
    
    lundi 23 mai 2011 14:57
  • "Kazun" wrote in message
    news:6f5c009f-0688-4b39-aabb-3dcb4218c064@communitybridge.codeplex.com...
    > No for all:
    > Param(
    > [parameter(Position=0)]
    > [string]$p1,
    > [parameter(Position=1)]
    > [string]$p2,
    > [parameter(Position=2)]
    > [string]$p3
    > )
     
    So it looks like I'm forcing them to be positional, but I'd like to be able
    to call them in any order, as long as the names are correct. e.g.
     
    .\PsMyScript.ps1 -p2 "two" -p3 "three" -p1 "one"
     
     
    lundi 23 mai 2011 15:30
  • "Kazun" wrote in message
    news:6f5c009f-0688-4b39-aabb-3dcb4218c064@communitybridge.codeplex.com...
    > No for all:
    > Param(
    > [parameter(Position=0)]
    > [string]$p1,
    > [parameter(Position=1)]
    > [string]$p2,
    > [parameter(Position=2)]
    > [string]$p3
    > )
     
    So it looks like I'm forcing them to be positional, but I'd like to be able
    to call them in any order, as long as the names are correct. e.g.
     
    .\PsMyScript.ps1 -p2 "two" -p3 "three" -p1 "one"
     
     
    Then show on a concrete example of what is not working?
    lundi 23 mai 2011 15:46
  • Thanks for the help. The goal is to only set a parameter when it's name has
    been specified on the command line. This doesn't seem to be working.
     
    #PsTest.ps1
    param(
     [parameter(position=0)][string]$p1,
     [parameter(position=1)][string]$p2,
     [parameter(position=2)][string]$p3
    )
     
    write-host p1 : $p1
    write-host p2 : $p2
    write-host p3 : $p3
     
    Now call it with three unamed params
     
    .\PsTest.ps1 two one three
     
    In my test, the params will be populated, even though I didn't supply their
    names! I want to force the caller to use this notation.
     
    .\PsTest.ps1 -p2 two -p1 one -p3 three
     
    Further to this, the documentation seems wrong. It states
     
    about_Functions_Advanced_Parameters
     
    Position Named Argument
    "If this argument is NOT specified, the parameter name or its alias MUST be
    explicitly specified when the parameter is set".
     
    This implies that there's no way the param can be populated unless you've
    named it?
     
     
    mardi 24 mai 2011 09:39
  • You may use old style as in the others language is parse $args.

    Example for C++ - http://msdn.microsoft.com/en-us/library/17w5ykft.aspx

    mardi 24 mai 2011 11:04
  • "Kazun" wrote in message
    news:0b5ee9d4-3691-4059-aea2-1d11fbbed197@communitybridge.codeplex.com...
    > You may use old style as in the others language is parse $args.
    >
     
    Thanks, but I was hoping the new facilities in PowerShell 2.0 would have
    everything needed for accurate passing of parameters. No one has actually
    confirmed whether my test script is behaving as expected, or whether the
    documentation is correct (see above).
     
     
    mardi 24 mai 2011 12:20
  • Why would you want to do that?

    By nature all parameters are positional even if not explicitly declared as such.
    This allows you to specify just the arguments (values will bound to the respective parameters by the order they were written on the command line).
    You cannot disable positional parameter binding or force the command to accept only parameters that were specified by their names.

    Can you clarify your needs?
    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar
    mardi 24 mai 2011 13:02
  • from: get-help about_Functions_Advanced_Parameters
     
    <#
    Position Named Argument
    The Position argument specifies the position of the parameter.
    If this argument is not specified, the parameter name or its
    alias must be explicitly specified when the parameter is set.
    Also, if none of the parameters of a function have positions,
    the Windows PowerShell runtime assigns positions to each parameter
    based on the order in which the parameters are received.
    #>
     
    That last sentence about the runtime assignment of positions is
    what is complicating creating the scenario of making all parameters
    to a script be named only.
     
    mardi 24 mai 2011 13:04
  • You can come close to requiring all parameters to be named,
    but apparently you are required to allow for at least one
    positional parameter.
     
    Consider for example,
    param([parameter(Position=0)]$first, $a, $b);$first;$a;$b
     
    as the content of test.ps1
     
    Then you get
    C:..\WindowsPowerShell> test.ps1 0 1 2
    C:\Documents and Settings\Larry\My Documents\WindowsPowerShell\test.ps1
    : A positional parameter cannot be found that accepts argument '1'.
     
    C:..\WindowsPowerShell> test.ps1 0 -a 1 2
    C:\Documents and Settings\Larry\My Documents\WindowsPowerShell\test.ps1
    : A positional parameter cannot be found that accepts argument '2'.
     
    C:..\WindowsPowerShell> test.ps1 0 -a 1 -b 2
    0
    1
    2
     
     
    mardi 24 mai 2011 13:15
  • "Shay Levi [MVP]" wrote in message
    news:175101bd-56b3-4f88-8ac8-dd4f8caf5061@communitybridge.codeplex.com...
    > Why would you want to do that?
     
    > By nature all parameters are positional even if not explicitly declared as
    > such.
    > This allows you to specify just the arguments (values will bound to the
    > respective parameters by the order they were written on the command line).
     
    .\PsMyScript.ps1 -p2 "two" -p1 "one" -p3 "three" # good
    .\PsMyScript.ps1 "two" "one" "three" # not good!
     
    Why do I want to force named parameters? Because it's a good way of
    preventing mistakes made by the caller. Microsoft use it in their own
    command line tools (Vista and above). e.g. the Tasklist command does not
    accept positional parameters. In WSH you can do this. I'd be surprised if
    it's not possible in PowerShell.
     
    > You cannot disable positional parameter binding or force the command to
    > accept only parameters that were specified by their names.
    >
    > Can you clarify your needs?
    > --------------------------------------------------------------------------------
    > Shay Levy [MVP]
    > PowerShay.com
    > PowerShell Toolbar
     
     
    mardi 24 mai 2011 13:25
  • You'll have to implement it yourself, here's an example


    function Test-NamedParameters
    {
        Param(
            [string]$p1,
            [string]$p2,
            [string]$p3
        )

        $p = [System.Management.Automation.PsParser]::Tokenize($MyInvocation.Line,[ref]$null) | ? {$_.Type -match 'CommandParameter|CommandArgument'} | group type -NoElement  
      
        $hash = @{}  
        if($p) { $p | foreach {$hash[$_.Name]=$_.Count} }  

        # disable calling the function without any parameters
        if(!$hash.CommandParameter -and !$hash.CommandArgument)
        {
            Write-Error "no parameters nor arguments were found"
            return
        }  

        if($hash['CommandParameter'] -ne $hash['CommandArgument'])
        {
            Write-Error "parameter count doesn't match argument count"
            return
        }



        $p1
        $p2
        $p3
    }


    Test-NamedParameters #not allowed
    Test-NamedParameters -p1 one two three #not allowed
    Test-NamedParameters -p1 one -p2 two -p3 three #works
    Test-NamedParameters -p2 two -p3 three  -p1 one #works
    Test-NamedParameters -p3 three #works


    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar
    mardi 24 mai 2011 13:41
  • You can do something like this in your script:

    param(
    $bad_param,
    [string]$p1,
    [string]$p2,
    [string]$p3
    )

    if ($bad_param){write-host "Bad parameter";exit}

    Any unnamed parameter will get picked up by $bad_param.


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    • Modifié mjolinor mardi 24 mai 2011 15:07
    • Proposé comme réponse jrich jeudi 26 mai 2011 12:53
    • Marqué comme réponse Alan ZhuModerator vendredi 27 mai 2011 03:25
    mardi 24 mai 2011 13:55
  • Good idea (but did you mean to code parenthesis instead
    of braces in the param block?)
     
    mardi 24 mai 2011 14:43
  • Didn't mean to (copied from the OPs original post).
    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
    mardi 24 mai 2011 14:58
  • great idea, I like this approach.

    mardi 24 mai 2011 15:21
  • mjolinor wrote:
    > Didn't mean to (copied from the OPs original post).
     
    Oops. Sorry about that. I'm used to typing curly brackets in PowerShell.
     
    > --------------------------------------------------------------------------------
    > [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775
    > 0645570").substring(($_*2),2))})-replace " "
     
     
    mercredi 25 mai 2011 19:52
  • This reminds me of a PowerShell inconsistency.
    Parentheses and commas are used for both ways that are provided to
    associate formal parameters with functions in PowerShell, yet when you
    call that function, you do not use parentheses to wrap the list of
    actual parameters. For example:
     
    function args_form1($a1, $a2){ '>'+$a1+'<';'>'+$a2+'<'}
    function args_form2{param($a1, $a2);'>'+$a1+'<';'>'+$a2+'<'}
     
    args_form1 1 2
    >1<
    >2<
     
    args_form1(1, 2)
    >1 2<
    ><
     
    args_form2 1 2
    >1<
    >2<
     
    args_form2(1, 2)
    >1 2<
    ><
     
    Perhaps
     
    function args_form1 $a1 $a2 { '>'+$a1+'<';'>'+$a2+'<'}
    function args_form2{param $a1 $a2; '>'+$a1+'<';'>'+$a2+'<'}
     
    would have been better as the starting point for the argument
    declaration syntax. Oh well; it is what it is.
     
    On 5/25/2011 2:52 PM, Gerry Hickman wrote:
    > Oops. Sorry about that. I'm used to typing curly brackets in PowerShell.
     
    mercredi 25 mai 2011 22:35
  • mjolinor, awesome trap :)
    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar
    jeudi 26 mai 2011 06:40
  • "mjolinor" wrote in message
    news:cf01c476-1831-4270-b462-ac7612240a56@communitybridge.codeplex.com...
    > You can do something like this in your script:
    >
    > param(
    > $bad_param,
    > [string]$p1,
    > [string]$p2,
    > [string]$p3
    > )
    >
    > if ($bad_param){write-host "Bad parameter";exit}
    >
    > Any unnamed parameter will get picked up by $bad_param.
     
    Thanks, this is a good work-around!
     
     
    mercredi 1 juin 2011 08:51
  • FYI

     

    http://blogs.microsoft.co.il/blogs/scriptfanatic/archive/2011/09/19/how-to-disable-positional-parameter-binding-in-powershell.aspx


    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar
    lundi 19 septembre 2011 18:18