Windows Server TechCenter > Windows Server Forums > Windows PowerShell > Named Parameters not working as expected

Answered Named Parameters not working as expected

  • Monday, May 23, 2011 1:36 PM
     
     
    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.
     
     

Answers

  • Tuesday, May 24, 2011 1:55 PM
     
     Answered

    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 " "

    • Edited by mjolinor Tuesday, May 24, 2011 3:07 PM
    • Proposed As Answer by jrich Thursday, May 26, 2011 12:53 PM
    • Marked As Answer by Alan ZhuModerator Friday, May 27, 2011 3:25 AM
    •  

All Replies

  • Monday, May 23, 2011 1:59 PM
     
     

    Try using - Position.

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

     

    get-help about_Functions_Advanced_Parameters

  • Monday, May 23, 2011 2:50 PM
     
     
    "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...
     
     
  • Monday, May 23, 2011 2:57 PM
     
      Has Code
    No for all:
    Param(
    	[parameter(Position=0)]
    	[string]$p1,
    	[parameter(Position=1)]
    	[string]$p2,
    	[parameter(Position=2)]
    	[string]$p3
    	)
    
  • Monday, May 23, 2011 3:30 PM
     
     
    "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"
     
     
  • Monday, May 23, 2011 3:46 PM
     
     
    "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?
  • Tuesday, May 24, 2011 9:39 AM
     
     
    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?
     
     
  • Tuesday, May 24, 2011 11:04 AM
     
     

    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

  • Tuesday, May 24, 2011 12:20 PM
     
     
    "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).
     
     
  • Tuesday, May 24, 2011 1:02 PM
    Moderator
     
     
    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
  • Tuesday, May 24, 2011 1:04 PM
     
     
    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.
     
  • Tuesday, May 24, 2011 1:15 PM
     
     
    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
     
     
  • Tuesday, May 24, 2011 1:25 PM
     
     
    "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
     
     
  • Tuesday, May 24, 2011 1:41 PM
    Moderator
     
     

    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
  • Tuesday, May 24, 2011 1:55 PM
     
     Answered

    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 " "

    • Edited by mjolinor Tuesday, May 24, 2011 3:07 PM
    • Proposed As Answer by jrich Thursday, May 26, 2011 12:53 PM
    • Marked As Answer by Alan ZhuModerator Friday, May 27, 2011 3:25 AM
    •  
  • Tuesday, May 24, 2011 2:43 PM
     
     
    Good idea (but did you mean to code parenthesis instead
    of braces in the param block?)
     
  • Tuesday, May 24, 2011 2:58 PM
     
     
    Didn't mean to (copied from the OPs original post).
    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
  • Tuesday, May 24, 2011 3:21 PM
     
     

    great idea, I like this approach.

  • Wednesday, May 25, 2011 7:52 PM
     
     
    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 " "
     
     
  • Wednesday, May 25, 2011 10:35 PM
     
     
    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.
     
  • Thursday, May 26, 2011 6:40 AM
    Moderator
     
     
    mjolinor, awesome trap :)
    Shay Levy [MVP]
    PowerShay.com
    PowerShell Toolbar
  • Wednesday, June 01, 2011 8:51 AM
     
     
    "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!
     
     
  • Monday, September 19, 2011 6:18 PM
    Moderator
     
     

    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