header1   header
header
header Register : : Login header
header
connector   connector
menuleft menuright
submenu   submenu
left
Give me getopts or give me frustration
Last Post 16 Jun 2011 04:39 AM by halr9000. 5 Replies.
Printer Friendly
  •  
  •  
  •  
  •  
  •  
Sort:
PrevPrev NextNext
You are not authorized to post a reply.
Author Messages
AndyUser is Offline
New Member
New Member
Posts:1
Avatar

--
12 Jun 2011 07:08 AM
    I really hate the idea of using positional values for command line arguments.

    Has anyone found or developed an equivalent to Perl getopt?

    I could always write something but I'd rather not have to. :)
    halr9000User is Offline
    PowerShell MVP, Site Admin
    Advanced Member
    Advanced Member
    Posts:565
    Avatar

    --
    12 Jun 2011 09:34 AM
    I suggest you check out the "about_functions" help document. It describes several of the options available for accepting parameters as input. You have positional (frankly rare with PowerShell), named, switches, and more. At a prompt, type "help about_functions" or go here: http://technet.microsoft.com/en-us/...47712.aspx
    Community Director, PowerShellCommunity.org
    Co-host, PowerScripting Podcast
    Author, TechProsaic
    whertzing56User is Offline
    New Member
    New Member
    Posts:48
    Avatar

    --
    14 Jun 2011 06:48 PM
    I miss getopts too, for command line arguments to scripts. But even getopts didn't have everything I wanted when I was writing perl scripts. I'm trying to figure out a framework that will let me

    1) define default values for command line arguments in the script itself
    2) define "MyScript.SystemPreferences.ps1" that lives in the same dir as the script, containing some or all of the script's argument values
    3) define "MyScript.UserPreferences.ps1" that lives in the user's powershell path subdirs (like (resolve-path "[Environment]::GetFolderPath("MyDocuments")\WindowsPowerShell").Path
    4) accept named argument values defined in the Environment
    5) accept a ps1 file on the command line containing some or all of the argument values
    6) accept individual named parameters on the command line

    The idea is that each is additive, the higer number having higher priority, and (somehow), the higher priority mechanisms can either augment or completly replace a lower priority value. It should work for modules as well as scripts, so if you build up a script from various modules, you can specify individual preference files for each module and/or include module arguments in the parent script's Preferences file or command line. (Whew, that last is not going to be easy)

    I had written a module with some of these capabilities for Perl that extended getopts (a decade ogo!), and I've been studying the problem in Powershell (still a newbie). You interested in a collaboration on this? Anybody else worked along these lines? I've seen some interesting code in the latest ShowUI modules for function arguments I'm still trying to wrap my head around...
    EBGreenUser is Offline
    Veteran Member
    Veteran Member
    Posts:1092
    Avatar

    --
    15 Jun 2011 05:26 AM
    Here is a quick first pass through your requirements:

    1) define default values for command line arguments in the script itself

    The param block will let you set a default value:

    param(
    $foo = 'bar'
    )

    2) define "MyScript.SystemPreferences.ps1" that lives in the same dir as the script, containing some or all of the script's argument values

    You could simply dot source the file in your script:

    if(Test-Path .\MyScript.SystemPreferences.ps1){
    . .\MyScript.SystemPreferences.ps1
    }

    3) define "MyScript.UserPreferences.ps1" that lives in the user's powershell path subdirs (like (resolve-path "
    [Environment]::GetFolderPath("MyDocuments")\WindowsPowerShell").Path

    See #2

    4) accept named argument values defined in the Environment

    Not sure exactly what you want for this one

    5) accept a ps1 file on the command line containing some or all of the argument values

    Just add a $ParamFile (or whatever name you want) variable to your params then dot source it just like #2

    6) accept individual named parameters on the command line

    Anything in the param block can be passed at the command line


    The real issue is with working out the priority. That really is just an issue of the order that you check things in the script.
    "Look Ma...no strings!"
    whertzing56User is Offline
    New Member
    New Member
    Posts:48
    Avatar

    --
    15 Jun 2011 08:54 PM
    "The real issue is with working out the priority".. - Aye - there's the rub... For in that prioritization, who knows what subtle bugs may come to trouble our dreams... Gawd, the Bard is spinning in his grave...

    Seriously - I'd like to have a hash $defaultargs = {paramname1 = paramvalue1; paramname2 = paramvalue2) in the script/module.
    Next, create a copy $currentargs = $defaultargs.Clone().
    Next, look for the file MyScript.SystemPreferences.ps1 in it's various places (priority given to the current dir, then the dir where the top-level script lives, then the regular PATH subdirs and Powershell subdirs), and merge with $currentargs
    Next, look for the file MyScript.UserPreferences.ps1 in the same various places), and merge with $currentargs
    Next, look in the environment for any variables of name "paramname1" or "paramname2" (walk the keys of $currentargs). Some Automated test tools would like to set parameter name/value pairs in the environment
    Next, see if a $ParamFile was passed on the command line, and merge with $currentargs
    Next, see if any named parameters are present on the command line, and merge with $currentargs
    Finally, execute the module or function with $currentargs

    Why so complex?
    BuiltInDefaults are the ultimate fallback in nothing else is specified.
    System Preferences let the script writer define (and later extend) default values of any parameters (I have in mind here a set of SQL query strings - distribute an initial set, and later release additional queries)
    User Preferences let the script consumer add their own values to the parameters
    Environment Variables - really just for completeness, I've seen automated testing/regression testing harnesses that like to set script arguments using environment variables
    Argument File specified on the command line - While developing or extending the paramter default values, specify the new default values in a file and pass it on the command line to the script
    Individual Arguments specified by name on the command line - the traditional way

    Writing this would be straightforward, but here's where I lose my way... If there is an argument $SQLCommandStrings = @{cmd1='Select 1'; cmd2='Select2'} as part of the builtin, how can I (simply/concisly) indicate that the next highest priority file should extend this argument by adding another key/value pair? and how should we indicate that the next highest priority file should replace the entire hashtable @{cmd1='Select 1'; cmd2='Select2'} with a different one @{cmd99='Select ninetynine'}.

    To make this an actionable question...
    # given these from the various preference files or command line file
    $lowpriargs = @{cmdstrings=@{cmd1='Select 1'; cmd2='Select2'}}
    $hipriargs = @{cmdstrings=@{cmd99='Select ninetynine'}}
    # there are two outcomes of merging: augmented or supplanted(replaced)
    $mergedargs_augmented=@{cmdstrings=@{cmd1='Select 1'; cmd2='Select2';cmd99='Select ninetynine'}}
    $mergedargs_supplanted=@{cmdstrings=@{cmd99='Select ninetynine'}}

    How can we specify in the preferences file, perhaps as an attribute for each argument, that tells the module/script to augment the lower priority value with the higher priority value,or supplant it?

    and finally - does anybody beside Andy (the OP) and myself even care? getopts was a great Perl module, but it's not quite the Powershell way. And I'm not interested in writing a Pwoershell getopts - What I really want is a way to cascade/combine parameter default values, so that I (the script author) can define a set of default values, my users can augment/extend/replace them, and later I can update/extend the default values, all without having to modify the script/module, just modify the various preferences file(s). Maybe somebody can point me to some prior work in this area?
    halr9000User is Offline
    PowerShell MVP, Site Admin
    Advanced Member
    Advanced Member
    Posts:565
    Avatar

    --
    16 Jun 2011 04:39 AM
    Check out splatting (a powershell v2 feature). Splatting takes a hashtable and applies the keys as parameter names and their values as parameter values. You supply it in one go like so:

    $foo = @{
    Name = 'value1'
    Color = 'blue'
    Number = 43
    OtherThing = ( Get-Process powershell ) # or whatever
    }

    Get-Bar @foo

    As ebgreen suggests, you can do work in a separate file and dot-source it. So, you could define $foo in that file, or you can have that file check $env for the presence of certain variables, and if present, put them in the hashtable.

    Note the splatting doesn't affect parameters not defined in the hashtable. Let's say that Get-Bar was defined like so:

    function Get-Bar {
    Param(
    $Name = 'default name',
    $Color = 'red',
    $Number = '0.34',
    $OtherThing,
    [string[]]$Filter = @( 1, 'two', 'three', 0.4 ),
    $ComputerName = 'localhost'
    )
    }

    In the splatting example above, I only defined 4 of the 6 possible parameters. In that case, the Filter and ComputerName parameters will use the defaults that I just defined here.
    Community Director, PowerShellCommunity.org
    Co-host, PowerScripting Podcast
    Author, TechProsaic
    You are not authorized to post a reply.


    Active Forums 4.3
    right
    footer   footer
    footer Sponsored by Quest Software • SAPIEN Technologies • Compellent • Microsoft Windows Server 2008 R2 footer
    footer   footer