This post is part of the Second Wednesday Demo Session, Click here for more info about additional demo posts.
If you have a set of commands or code that you want to reuse multiple times in a script or in your session you can place the code in a function.This allows you to call the function instead of typing the same piece of code over and over again. Just as an example, we’ll use Get-Process and display the Process Name and Company property.
PS > $Name = "powershell" PS > $process = Get-Process $Name PS > Write-Host ProcessName: $Process.Name ProcessName: powershell PS > Write-Host Company: $Process.Company Company: Microsoft CorporationInstead of typing the lines above everytime we want to check the Name and Company of a process we can place the code in a function.
function Get-Company([string]$Name) {
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
Now we can use the function instead.
PS > Get-Company -Name powershell ProcessName: powershell Company: Microsoft CorporationCalling a function in PowerShell is similar to calling one of the built-in cmdlets. You type the functions name followed by one or more parameters. You can also use Get-Help to find out additional information about a function, just as you do with CmdLets.
PS > Get-Help Get-Company Get-Company [[-Name]CmdLets in powershell usually have a set of common parameters such as Verbose, Debug, OutVariable and so on. You can add these additional parameters by using the [CmdletBinding()] attribute.]
function Get-Company {
[cmdletbinding()]
param([string]$Name)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
If we use the Get-Help CmdLet additional parameters are available in our function.
PS > Get-Help Get-Company Get-Company [[-Name]It’s also possible to set a parameter as mandatory. Here’s an example.] [-Verbose] [-Debug] [-ErrorAction ] [-WarningAction ] [-ErrorVariable ] [-WarningVariable ] [-OutVariable ] [-OutBuffer ]
function Get-Company {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
If we call the function without a value to the Name parameter we are automatically prompted for it.
PS > Get-Company cmdlet Get-Company at command pipeline position 1 Supply values for the following parameters: Name: powershell ProcessName: powershell Company: Microsoft CorporationIf we want to validate the input to a parameter we can use the [ValidateSet()] attribute. Here’s an example.
function Get-Company {
param(
[Parameter(Mandatory = $true)]
[ValidateSet("powershell","notepad")]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
The example above only accepts “powershell” or “notepad” as input.
PS > Get-Company -Name powershell ProcessName: powershell Company: Microsoft CorporationIf we type anything else an error occurs.
PS > Get-Company -Name calc
Get-Company : Cannot validate argument on parameter 'Name'. The argument "calc" does
not belong to the set "powershell,notepad" specified by the ValidateSet attribute.
Supply an argument that is in the set and then try the command again.
At line:1 char:18
+ Get-Company -Name <<<< calc
+ CategoryInfo : InvalidData: (:) [Get-Company], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Get-Company
We can also position the parameter. This applies when we provide parameter values without specifying the parameter.
function Get-Company {
param(
[Parameter(Position = 0)]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
PS > Get-Company powershell
ProcessName: powershell
Company: Microsoft Corporation
We can also let the function accept input from a pipeline by using ValueFromPipeLine.
function Get-Company {
param(
[Parameter(
Position = 0,
ValueFromPipeLine= $true)]
[string]$Name
)
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
Now we can use a pipeline when calling the function.
PS > "powershell" | Get-Company ProcessName: powershell Company: Microsoft CorporationWhat if we send two inputs through the pipeline?
PS > "powershell","winword" | Get-Company ProcessName: WINWORD Company: Microsoft CorporationNote that only the last input value was returned by the function. Adding a Process block solves this.
function Get-Company {
param(
[Parameter(
Position = 0,
ValueFromPipeLine= $true)]
[string]$Name
)
Process {
$process = Get-Process $Name
Write-Host ProcessName: $Process.Name
Write-Host Company: $Process.Company
}
}
Now we can send additional values through the pipeline.
PS > "powershell","winword" | Get-Company ProcessName: powershell Company: Microsoft Corporation ProcessName: WINWORD Company: Microsoft CorporationWe can also use propertynames from an object passed through a pipeline. Here's an example.
function Get-Company {
param(
[Parameter(
ValueFromPipeLineByPropertyName= $true)]
[string]$Name,
[Parameter(
ValueFromPipeLineByPropertyName= $true)]
[string]$Company
)
Process {
Write-Host ProcessName: $Name
Write-Host Company: $Company
}
}
Now we can use Get-Process and pipe the objects to our function as shown below.
PS > Get-Process powershell | Get-Company ProcessName: powershell Company: Microsoft Corporation PS > Get-Process powershell,winword | Get-Company ProcessName: powershell Company: Microsoft Corporation ProcessName: WINWORD Company: Microsoft CorporationIn an earlier example we took a look at how to add common parameters to a function, but how do we use them?
Well, it turns out that advanced functions in powershell give us access to the $pscmdlet automatic variable.
The variable exposes support for Verbose, Whatif and more. The function below calls the variable and displays it's properties.
function Get-PSCmdLet {
[cmdletbinding()]
param()
$psCmdLet
}
If we call the function, the variable properties are displayed.
PS > Get-PSCmdLet ParameterSetName : __AllParameterSets MyInvocation : System.Management.Automation.Invoc InvokeCommand : System.Management.Automation.Comma Host : System.Management.Automation.Inter SessionState : System.Management.Automation.Sessi Events : System.Management.Automation.PSLoc JobRepository : System.Management.Automation.JobRe InvokeProvider : System.Management.Automation.Provi Stopping : False CommandRuntime : Get-PSCmdLet CurrentPSTransaction : CommandOrigin : RunspaceIf we pipe the object to Get-Member we'll see all Methods and Properties available on the object.
PS > Get-PSCmdLet | Get-Member TypeName: System.Management.Automation.PSScriptCmdlet Name MemberType Definition ---- ---------- ---------- CurrentProviderLocation Method System.Managem Dispose Method System.Void Di Equals Method bool Equals(Sy GetDynamicParameters Method System.Object GetHashCode Method int GetHashCod GetResolvedProviderPathFromPSPath Method System.Collect GetResourceString Method string GetReso GetType Method type GetType() GetUnresolvedProviderPathFromPSPath Method string GetUnre GetVariableValue Method System.Object Invoke Method System.Collect ShouldContinue Method bool ShouldCon ShouldProcess Method bool ShouldPro ThrowTerminatingError Method System.Void Th ToString Method string ToStrin TransactionAvailable Method bool Transacti WriteCommandDetail Method System.Void Wr WriteDebug Method System.Void Wr WriteError Method System.Void Wr WriteObject Method System.Void Wr WriteProgress Method System.Void Wr WriteVerbose Method System.Void Wr WriteWarning Method System.Void Wr CommandOrigin Property System.Managem CommandRuntime Property System.Managem CurrentPSTransaction Property System.Managem Events Property System.Managem Host Property System.Managem InvokeCommand Property System.Managem InvokeProvider Property System.Managem JobRepository Property System.Managem MyInvocation Property System.Managem ParameterSetName Property System.String SessionState Property System.Managem Stopping Property System.BooleanNow let's see how we can use this in our function. Say we want to write to the Verbose message stream. We can do this by simply using the WriteVerbose() method as shown below.
function Get-Company {
[cmdletbinding()]
param([string]$Name)
$process = Get-Process $Name
$psCmdLet.WriteVerbose("ProcessName: $($Process.Name)")
$psCmdLet.WriteVerbose("Company: $($Process.Company)")
}
When we call the function nothing seems to happen.
PS > Get-Company -Name powershellBut if we add the -Verbose parameter the verbose messages are returned.
PS > Get-Company -Name powershell -Verbose VERBOSE: ProcessName: powershell VERBOSE: Company: Microsoft Corporation
[?]