在 PowerShell 中实现子命令模式
Implement the subcommand pattern in PowerShell
是否可以在 PowerShell 中实现子命令模式?类似于:
command [subcommand] [options] [files]
示例:Git、svn、Homebrew
一般架构是什么?将实际工作委托给脚本块的单个函数?每个子命令都隔离在其自己的 PS1
文件中,该文件是主脚本的点源文件? PowerShell 的各种元数据函数(例如 Get-Command
)是否能够 'inspect' 子命令?
我想到了这个模式,并找到了两种方法。我没有找到
在我的实践中实际应用,所以研究是相当学术的。但
下面的脚本工作正常。
实现此模式(以其自己的方式)的现有工具是
scoop.
模式子命令实现了经典的命令行界面
app <command> [parameters]
此模式引入了一个提供命令的脚本 app.ps1
而不是在脚本库中提供多个脚本或函数,或者
模块。每个命令都是特殊子目录中的脚本,例如./命令.
获取可用命令
app
调用命令
app c1 [parameters of Command\c1.ps1]
获取命令帮助
app c1 -? # works with splatting approach
app c1 -help # works with dynamic parameters
脚本app.ps1可能包含命令使用的常用函数。
splat.ps1(因此 app.ps1)- 带有 splatting[=48 的图案=]
优点:
- 最少的代码和开销。
- 位置参数有效。
-?
按原样提供帮助(简短帮助)。
缺点:
- PowerShell v3+,splatting 在 v2 中很有趣。
dynamic.ps1(因此 app.ps1)- 具有动态参数的模式
优点:
- PowerShell v2+.
- TabExpansion 适用于参数。
缺点:
- 更多代码,更多运行时工作。
- 仅命名参数。
- 帮助
-help
。
脚本
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
动态。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
}
是否可以在 PowerShell 中实现子命令模式?类似于:
command [subcommand] [options] [files]
示例:Git、svn、Homebrew
一般架构是什么?将实际工作委托给脚本块的单个函数?每个子命令都隔离在其自己的 PS1
文件中,该文件是主脚本的点源文件? PowerShell 的各种元数据函数(例如 Get-Command
)是否能够 'inspect' 子命令?
我想到了这个模式,并找到了两种方法。我没有找到 在我的实践中实际应用,所以研究是相当学术的。但 下面的脚本工作正常。
实现此模式(以其自己的方式)的现有工具是 scoop.
模式子命令实现了经典的命令行界面
app <command> [parameters]
此模式引入了一个提供命令的脚本 app.ps1
而不是在脚本库中提供多个脚本或函数,或者
模块。每个命令都是特殊子目录中的脚本,例如./命令.
获取可用命令
app
调用命令
app c1 [parameters of Command\c1.ps1]
获取命令帮助
app c1 -? # works with splatting approach
app c1 -help # works with dynamic parameters
脚本app.ps1可能包含命令使用的常用函数。
splat.ps1(因此 app.ps1)- 带有 splatting[=48 的图案=]
优点:
- 最少的代码和开销。
- 位置参数有效。
-?
按原样提供帮助(简短帮助)。
缺点:
- PowerShell v3+,splatting 在 v2 中很有趣。
dynamic.ps1(因此 app.ps1)- 具有动态参数的模式
优点:
- PowerShell v2+.
- TabExpansion 适用于参数。
缺点:
- 更多代码,更多运行时工作。
- 仅命名参数。
- 帮助
-help
。
脚本
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
动态。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
}