Last active
July 3, 2017 13:24
-
-
Save turboBasic/b7facf3afabf584c91a5ab606850f868 to your computer and use it in GitHub Desktop.
Template for Powershell function with constants block which you can override while invoking and add parameters of any kind from Command line
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .SYNOPSIS | |
| Template of Function which takes any number of parameters | |
| .DESCRIPTION | |
| Template of Function which takes any number of parameters. | |
| Function has default parameter block $defaults which can be overridden by command line parameters. | |
| Function accumulates command line parameters and merges them with default parameters to the $__ENV.env hashtable. | |
| .PARAMETER ShowProperty | |
| Select one of 5 views of resulting parameters: Env, Named, Positional, Default and All (TODO) | |
| .INPUTS | |
| Does not accept inputs from the pipeline | |
| .OUTPUTS | |
| Outputs [PSCustomObject] with all the data about resulting parameters set. | |
| .NOTES | |
| (c) 2017 Andriy Melnyk https://guthub.com/TurboBasic | |
| .LINK | |
| #Merge-Hastables | |
| .FUNCTIONALITY | |
| Use this for testing and learning how Powershell processes advanced function parameters. Due to fully working and universal | |
| feature set it is easy and convenient to use as a template function | |
| .EXAMPLE | |
| In all examples we assume that the Function doesn't have positional parameters and default parameters block looks as follows: | |
| $defaults = @{ | |
| SERVER = 'http://localhost' | |
| PORT = 8080 | |
| SOME_CONSTANT = 0xFF | |
| PRODUCTION = $False | |
| showProperty = 'Env' | |
| } | |
| PS C:\> Abstract-FunctionWithConfigBlockTemplate | |
| Name Value | |
| ---- ----- | |
| PORT 8080 | |
| SOME_CONSTANT 255 | |
| SERVER http://localhost | |
| PRODUCTION False | |
| showProperty Env | |
| .EXAMPLE | |
| By default, resulting set includes only named parameters: | |
| PS C:\> Abstract-FunctionWithConfigBlockTemplate -a 2 $true 4 | |
| Name Value | |
| ---- ----- | |
| PRODUCTION False | |
| SERVER http://localhost | |
| SOME_CONSTANT 255 | |
| showProperty Env | |
| PORT 8080 | |
| a 2 | |
| .EXAMPLE | |
| PS C:\> Abstract-FunctionWithConfigBlockTemplate -server 'https://8.8.8.8', 'http://local.dev' -port 80 -production | |
| Name Value | |
| ---- ----- | |
| PRODUCTION True | |
| SERVER {https://8.8.8.8, http://local.dev} | |
| SOME_CONSTANT 255 | |
| showProperty Env | |
| PORT 80 | |
| .EXAMPLE | |
| Show non-default Named parameters | |
| PS C:\> Abstract-FunctionWithConfigBlockTemplate -server 'https://8.8.8.8','http://localhost' -port 80 -production -showproperty named | |
| Key Value | |
| --- ----- | |
| __restParameters {-server, https://8.8.8.8 http://localhost, -port, 80...} | |
| server {https://8.8.8.8, http://localhost} | |
| port 80 | |
| production True | |
| showproperty named | |
| #> | |
| Function Abstract-FunctionWithConfigBlockTemplate { | |
| [OUTPUTTYPE( [string[]] )] | |
| [CMDLETBINDING( PositionalBinding=$False )] | |
| PARAM( | |
| #region Constants block. | |
| [PARAMETER()] | |
| [VALIDATESCRIPT({ | |
| If( $_ -is [System.Collections.Hashtable] ) | |
| { $True } | |
| Else | |
| { Throw "'$_' should be a Hashtable!" } | |
| })] | |
| $defaults = @{ | |
| SERVER = 'http://localhost' | |
| PORT = 8080 | |
| SOME_CONSTANT = 0xFF | |
| PRODUCTION = $False | |
| showProperty = 'Env' | |
| }, | |
| # listEnv = $True | |
| # listNamed = $False | |
| # listDefault = $False | |
| # listPositional = $False | |
| #endregion | |
| #region Standard parameters | |
| <# | |
| add `[PARAMETER( Position=<number> )]` if you need positional parameter. | |
| In this case custom parameters will start to accumulate after the last positional parameter | |
| and you will have to include postional parameters to command line | |
| Eg. if you have 2 positional parameters | |
| [PARAMETER(Position=0)] $a | |
| [PARAMETER(Position=1)] $b | |
| then the bindings for the following commands will look as follows: | |
| PS> Abstract-FunctionWithConfigBlockTemplate -customArg 2 3 4 | |
| $__ENV.customArg = 2 | |
| $a = 3 | |
| $b = 4 | |
| PS> Abstract-FunctionWithConfigBlockTemplate 2 3 4 | |
| $__ENV.anonymousParameter = 4 | |
| $a = 2 | |
| $b = 3 | |
| #> | |
| [PARAMETER( ValueFromPipeline )] # add `Position=<number>` if needed | |
| [Int[]] | |
| $SomeIntegerParameter = 137, | |
| [PARAMETER( )] # add `Position=<number>` if needed | |
| [String] | |
| $SomeTextParameter = 'Default text', | |
| #endregion | |
| #region Custom parameters | |
| [PARAMETER( ValueFromRemainingArguments )] | |
| [Object[]] | |
| $__restParameters | |
| #endregion | |
| ) | |
| BEGIN { | |
| # Current parameter binding state | |
| # | |
| # Ready: Previous parameter completely read. Ready to read next named or positional | |
| # parameter | |
| # | |
| # ParameterName: | |
| # Name of parameter has been read, expecting its value, if no value $True is default | |
| # | |
| enum ParameterBindingState { | |
| Ready | |
| ParameterName | |
| } | |
| # Global parameter binding state depends on current parameter state and current text token | |
| # (this is current parameter name) and previously read parameter name, if any | |
| $bindingState = @{ | |
| State = [ParameterBindingState]::Ready | |
| Name = '' | |
| } | |
| $Parameters = Foreach ($Parameter in $__restParameters) { | |
| if( $Parameter -match '^-([a-z_][a-z0-9_]*)$' ) { | |
| $bindingState.Name = $Matches[1] | |
| New-Variable -Name $bindingState.Name -Value $True | |
| $PSBoundParameters.Add( $bindingState.Name, $True ) | Out-Null | |
| $bindingState.State = [ParameterBindingState]::ParameterName | |
| } elseif( $bindingState.State -eq [ParameterBindingState]::ParameterName ) { | |
| $Value = $foreach.Current | |
| Set-Variable -Name $bindingState.Name -Value $Value | |
| $PSBoundParameters[$bindingState.Name] = $Value | |
| $bindingState.State = [ParameterBindingState]::Ready | |
| $bindingState.Name = '' | |
| } elseif( $bindingState.State -eq [ParameterBindingState]::Ready ) { | |
| $Parameter | |
| } | |
| } | |
| $__ENV = @{} | |
| $__ENV.Add('Named', $psBoundParameters) | |
| $__ENV.Add('Positional', $Parameters) | |
| $__ENV.Add('Default', $defaults ) | |
| } | |
| PROCESS { | |
| $null = 'Do something with arguments' | |
| } | |
| END { | |
| $__ENV.Add( 'Env', (Merge-Hashtables $__ENV.Default $__ENV.Named) ) | |
| $__ENV.Env.Remove('__restParameters') | |
| # $__ENV | Format-Table -Autosize -Wrap ` | |
| # Name, @{ Label = "Value"; Expression = { ConvertTo-JSON $_.Value } } | |
| $includeProperties = @() | |
| $includeProperties += ,'Env' * $__ENV.Env.listEnv | |
| $includeProperties += ,'Named' * $__ENV.Env.listNamed | |
| $includeProperties += ,'Default' * $__ENV.Env.listDefault | |
| $includeProperties += ,'Positional' * $__ENV.Env.listPositional | |
| ([psCustomObject]$__ENV) | Select -Property $includeProperties | Out-Null | |
| ([psCustomObject]$__ENV) | Select -ExpandProperty $__ENV.Env.showProperty | |
| } | |
| } | |
| # some short examples | |
| # (Abstract-FunctionWithConfigBlockTemplate 1 'tty' -15 @(1,2),2 @('cdf',5),1) | |
| # (Abstract-FunctionWithConfigBlockTemplate 1 'tty' -15 @(1,2),2 @('cdf',5),1).Positional | |
| # (Abstract-FunctionWithConfigBlockTemplate 1 'tty' -15 @(1,2),2 @('cdf',5),1).Positional[2] | |
| # (Abstract-FunctionWithConfigBlockTemplate 1 'tty' -15 @(1,2),2 @('cdf',5),1).Positional[3][1] | |
| # (Abstract-FunctionWithConfigBlockTemplate 1 'tty' -15 @(1,2),2 @('cdf',5),1).Named | |
| # (Abstract-FunctionWithConfigBlockTemplate 'tty' -x -15 -yy @(1,2),2 -zz -zzn @('cdf',5),1 -zlast).Named |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment