PowerShell's if...elseif...else statements are pretty standard, taking the form:
if ( <condition> ) {
} elseif ( <alternative condition> ) {
} else {
}
The conditions used within if...elseif...else statements can be any combination of
and
operators, with parenthesis used to control operator precedence.
Good programming practice dictates that if...elseif...else shouldn't exceed a handful of conditions. If you
find yourself writing a long if...elseif...else statement, consider using a
statement.
PowerShell's switch statement (sometimes referred to as a case statement) operates in multiple ways, the first
of which will be familiar to anyone who has used C, or one of its syntactical derivatives. All of the variants will be
described below.
In its simplest form, PowerShell's switch statement looks very familiar:
switch -exact ( <expression> ) {
<case 1> {
break;
}
<case 2> {
break;
}
default {
}
}
This is the default mode of operation, whereby a case will only be executed if it matches a given value or the result of a given expression.
Textual comparisons
are case-insensitive, unless the -casesensitive directive is specified. Being the default mode of operation, the switch statement
will operate in this manner, even if the -exact directive isn't explicitly specified. The use of the break
statement in any given case is optional, and simply halts evaluation flow, therefore preventing any of the subsequent cases being evaluated.
The following example demonstrates a very simple switch statement with no cascading evaluations:
[Int32] $intSelection = Read-Host -Prompt 'Enter a number between 1 and 5';
switch -exact ( $intSelection ) {
1 { 'You entered one.'; break; }
2 { 'You entered two.'; break; }
3 { 'You entered three.'; break; }
4 { 'You entered four.'; break; }
5 { 'You entered five.'; break; }
default { 'Invalid input.'; break; }
}
However, by removing the break statements, we can cause multiple cases to be evaluated. This is particularly useful
when using expressions instead of constants for one or more of the cases, for example:
[Int32] $intSelection = Read-Host -Prompt 'Enter a number between 1 and 5';
switch -exact ( $intSelection ) {
1 { 'You entered one.'; }
2 { 'You entered two.'; }
3 { 'You entered three.'; }
4 { 'You entered four.'; }
5 { 'You entered five.'; }
{ $_ -lt 3 } { 'Your number is less than 3.' };
{ $_ -gt 3 } { 'Your number is greater than 3.' };
{ $_ % 2 } { 'Your number is odd.'; }
{! ($_ % 2) } { 'Your number is even.'; }
default { 'Invalid input.'; }
}
The -wildcard option allows you to use simple pattern matches to evaluate cases. The pattern match syntax is the same
as that of the
operator, that is, ?, *, [a-e] (range) and [aeiou] (set).
[String] $strWord = Read-Host -Prompt 'Enter a word';
switch -wildcard ( $strWord ) {
'a*' { 'Begins with A.'; }
'b*' { 'Begins with B.'; }
'c*' { 'Begins with C.'; }
'?c*' { 'Second letter is C.'; }
'[h-m]*' { 'First letter is between H and M.'; }
'[aeiou]*' { 'First letter is a vowel.'; }
'*s' { 'Last letter is S.'; }
default { 'Other.' }
}
The -regex option is similar to the -wildcard option, except that it uses
pattern matching.
[String] $strRegNum = Read-Host -Prompt 'Enter a UK car registration number';
switch -regex ( $strRegNum ) {
'^[a-z]{1,2}\s?\d{1,4}$' { 'Pre-1932.'; break; }
'^[a-z]{3} \d{1,2}$' { '1932-1963'; break; }
'^[a-z]{3} \d{1,3}[a-z]$' { '1960-1982'; break; }
'^[a-z]\d{2,3} [a-z]{3}$' { '1983-2001'; break; }
'^[a-z]{2}\d{2} [a-z]{3}$' { '2002+'; break; }
default { 'Unrecognised registration format.' }
}
The -file option causes the switch statement to evaluate each line of the given file against
its defined cases. This mode of operation, when combined with the -regex is useful for parsing
configuration files, for example:
[String] $strInDir = '';
[String] $strOutDir = '';
[Int32] $intSleepPeriod = -1;
[Boolean] $boolAlertOnFailure = $false;
[Boolean] $boolConfigError = $false;
[String] $strConfigFile = 'c:\params.cfg';
switch -regex -file ($strConfigFile) {
'^in_dir=([c-z]:\\(?:\w+\\){0,}\w+\\?)$' { $strInDir = $matches[1]; continue; }
'^out_dir=([c-z]:\\(?:\w+\\){0,}\w+\\?)$' { $strOutDir = $matches[1]; continue; }
'^sleep_period=(\d{1,3})$' { $intSleepPeriod = [Convert]::ToInt32($matches[1]); continue; }
'^alert_on_failure=(yes|no)$' { $boolAlertOnFailure = ($matches[1] -eq 'yes'); continue; }
default {
Write-Warning -Message ('Invalid or unrecognised configuration parameter : "{0}". Aborting.' -f $_ );
$boolConfigError = $true;
break;
}
}
if ( ! $boolConfigError ) {
Write-Host -Object ( 'In directory is : {0}' -f $strInDir );
Write-Host -Object ( 'Out directory is : {0}' -f $strOutDir );
Write-Host -Object ( 'Sleep period is : {0}' -f $intSleepPeriod );
Write-Host -Object ( 'Alert on failure : {0}' -f $boolAlertOnFailure );
}
However, there is one subtlety with this mode of operation; this being the way that the break statement affects execution.
Using a break statement in any of the cases will cause the switch statement to cease processing any further cases AND any
further lines of the supplied file. If you wish to cease processing any further cases for the current line of the file,
but then continue with the next line of the file, you must use the continue statement (see example, above).
When presented with a compound data type, such as an array, the -switch statement will iterate through
each element of the given data, evaluating each value against its defined cases.
[System.ComponentModel.Component[]] $objProcesses = Get-Process | Sort-Object -Property WorkingSet -Descending;
switch ( $objProcesses ) {
{ $_.WorkingSet -gt 250MB } { 'The working set of process "{0}" is greater than 250MB.' -f $_.ProcessName; continue; }
{ $_.WorkingSet -gt 100MB } { 'The working set of process "{0}" is greater than 100MB.' -f $_.ProcessName; continue; }
{ $_.WorkingSet -gt 10MB } { 'The working set of process "{0}" is greater than 10MB.' -f $_.ProcessName; continue; }
{ $_.WorkingSet -gt 5MB } { 'The working set of process "{0}" is greater than 5MB.' -f $_.ProcessName; continue; }
default { 'The working set of process "{0}" is {1:N0} KBytes.' -f $_.ProcessName, ($_.WorkingSet / 1KB); }
}
Note that the continue and break commands have the same effect here as they do with the -file option.
A ternary operator provides a convenient way of returning one of two values, based on a given condition, and is often used
in place of an if...else, in order to aid readabilty of code. The most common syntax of a ternary operator
is that of the C programming language, i.e.:
( <condition> ? <expression_if_true> : <expression_if_false> )
Sadly, PowerShell doesn't have a ternary operator. This is a great shame. However, there is a simple trick
that will provide a similar outcome, this being:
@( <expression_if_false>, <expression_if_true> )[ <condition> ]
This trick (or hack?) utilises a two element array with a condition being used as an array index. The condition can evaluate to
$false (0) or $true (1), therefore returning the relevant array value.