Suppose I have a script with multiple functions taking the exact same parameters, in the same positions and with the same types and constraints, like this:
function Verb1-MyValue {
[CmdletBinding()]
param (
[parameter(Mandatory = $true)][String]$Param1,
[parameter(Mandatory = $true)][String]$Param2,
[ValidateSet("Value1","Value2")][String]$Param3 = "Value1"
)
# code ...
}
function Verb2-MyValue {
[CmdletBinding()]
param (
[parameter(Mandatory = $true)][String]$Param1,
[parameter(Mandatory = $true)][String]$Param2,
[ValidateSet("Value1","Value2")][String]$Param3 = "Value1"
)
# code ...
}
# and so on ...
I wanted to share the param
block with all functions to avoid potential problems (they need to be the same for all of them) and to avoid the redundancy.
Is there a way in PowerShell to share the param
block across multiple functions in the same script? If not, are there any alternatives to it?
If you are able to, I would recommend going with the "C# project which builds a powershell module" approach. Depending on your situation, there are a variety of benefits some of which include:
To get started, I found this article. Basically, it says to add a reference to System.Management.Automation.dll to your project, and then a very basic cmdlet would look as follows:
using System;
using System.Collection.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Management.Automation;
namespace MyModule
{
[Cmdlet(VerbsCommon.Get, "Saluation")]
public class GetSaluation : PSCmdlet
{
private string[] nameCollection;
[Parameter(
Mandatory = true,
ValueFromPipelineByPropertyName = true,
ValueFromPipelin = true,
Position = 0,
HelpMessage = "Name to get salutation for."
)]
[Alias("Person", "FirstName")]
public string[] Name
{
get { return nameCollection;}
set { nameCollection = value;}
}
protected override void BeginProcessing()
{
base.BeginProcessing();
}
protected override void ProcessRecord()
{
foreach (string name in nameCollection)
{
WriteVerbose("Creating salutation for " + name);
string salutation = "Hello, " + name;
WriteObject(salutation);
}
}
protected override void EndProcessing()
{
base.EndProcessing();
}
}
Then, to use this module, open a powershell prompt, navigate to where your dll is built and use the Import-Module cmdlet.
And then for your specific question (How do I reuse param blocks with different cmdlets?) you can have a base cmdlet which defines the parameters, and all of the cmdlets you wish to write can inherit that from the base class.
I'd recommend using Travis' suggestion and go the compiled cmdlet route. You asked in one of your comments if this was possible with just script, though, so I'm going to try to provide an example of doing that. Advanced functions support creating dynamic parameters, and you can combine that capability with the Get-Command cmdlet to create dynamic versions of a specified command's parameters:
function GetDynamicParamDictionary {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true, Mandatory=$true)]
[string] $CommandName
)
begin {
# Get a list of params that should be ignored (they're common to all advanced functions)
$CommonParameterNames = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([type] [System.Management.Automation.Internal.CommonParameters]) |
Get-Member -MemberType Properties |
Select-Object -ExpandProperty Name
}
process {
# Create the dictionary that this scriptblock will return:
$DynParamDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
# Convert to object array and get rid of Common params:
(Get-Command $CommandName | select -exp Parameters).GetEnumerator() |
Where-Object { $CommonParameterNames -notcontains $_.Key } |
ForEach-Object {
$DynamicParameter = New-Object System.Management.Automation.RuntimeDefinedParameter (
$_.Key,
$_.Value.ParameterType,
$_.Value.Attributes
)
$DynParamDictionary.Add($_.Key, $DynamicParameter)
}
# Return the dynamic parameters
$DynParamDictionary
}
}
function TestFunction {
# Create some dummy params
param(
[string] $StringParam,
[switch] $Switch1,
[switch] $Switch2,
[int] $IntParam
)
}
function MimicTestFunction {
[CmdletBinding()]
# Empty param block (you could add extra params here)
param()
# Mimic TestFunction's parameters
dynamicparam { GetDynamicParamDictionary TestFunction }
process { $PSBoundParameters }
}
function MimicGetChildItem {
[CmdletBinding()]
param()
dynamicparam { GetDynamicParamDictionary Get-ChildItem }
process { $PSBoundParameters }
}
This should work except when a the reference command also has dynamic parameters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With