Implement a subcommand template in PowerShell

Can I implement a subcommand template in PowerShell? Sort of:

command [subcommand] [options] [files] 

Examples: Git, svn, Homebrew

What will be the overall architecture? The only function that delegates the actual work to script blocks? Is each subcommand isolated in its own PS1 file, which are point sources from the primary script? Can various PowerShell metadata functions (such as Get-Command ) β€œcheck” subcommands?

+5
source share
1 answer

I thought about this template and found two ways to do this. I have not found real applications in my practice, so the research is quite academic. But the scripts below work fine.

An existing tool that implements this template (in its own way) is scoop .


The pattern subcommand implements the classic command line interface

 app <command> [parameters] 

This template introduces one script app.ps1 , which provides commands instead of providing multiple scripts or functions in a script library or module. Each command is a script in a special subdirectory, for example .. / Command.

Get available teams

 app 

Call team

 app c1 [parameters of Command\c1.ps1] 

Get team help

 app c1 -? # works with splatting approach app c1 -help # works with dynamic parameters 

script app.ps1 may contain common functions used by commands.


splat.ps1 (app.ps1 as such) - template with splatting

Pros:

  • Minimum code and service data.
  • Positional parameters work.
  • -? works for reference as is (short help).

Minuses:

  • PowerShell v3 + splatting works funny in v2.

dynamic.ps1 (app.ps1 as such) - a template with dynamic parameters

Pros:

  • PowerShell v2 +.
  • TabExpansion works for options.

Minuses:

  • More code, more runtime.
  • Named parameters only.
  • Help as -help .

Scenarios

splat.ps1

 #requires -Version 3 param( $Command ) if (!$Command) { foreach($_ in Get-ChildItem $PSScriptRoot\Command -Name) { [System.IO.Path]::GetFileNameWithoutExtension($_) } return } & "$PSScriptRoot\Command\$Command.ps1" @args 

dynamic.ps1

 param( [Parameter()]$Command, [switch]$Help ) dynamicparam { ${private:*pn} = 'Verbose', 'Debug', 'ErrorAction', 'WarningAction', 'ErrorVariable', 'WarningVariable', 'OutVariable', 'OutBuffer', 'PipelineVariable' $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Definition $Command = $PSBoundParameters['Command'] if (!$Command) {return} $_ = Get-Command -Name "$PSScriptRoot\Command\$Command.ps1" -CommandType ExternalScript -ErrorAction 1 if (!($_ = $_.Parameters) -or !$_.Count) {return} ${private:*r} = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary (${private:*a} = New-Object System.Collections.ObjectModel.Collection[Attribute]).Add((New-Object System.Management.Automation.ParameterAttribute)) foreach($_ in $_.Values) { if (${*pn} -notcontains $_.Name) { ${*r}.Add($_.Name, (New-Object System.Management.Automation.RuntimeDefinedParameter $_.Name, $_.ParameterType, ${*a})) } } ${*r} } end { if (!$Command) { foreach($_ in Get-ChildItem $PSScriptRoot\Command -Name) { [System.IO.Path]::GetFileNameWithoutExtension($_) } return } if ($Help) { Get-Help "$PSScriptRoot\Command\$Command.ps1" -Full return } $null = $PSBoundParameters.Remove('Command') & "$PSScriptRoot\Command\$Command.ps1" @PSBoundParameters } 
+7
source

All Articles