103

I basically want to do this:

switch($someString.ToLower())
{
    "y", "yes" { "You entered Yes." }
    default { "You entered No." }
}
0

11 Answers 11

122
switch($someString.ToLower()) 
{ 
    {($_ -eq "y") -or ($_ -eq "yes")} { "You entered Yes." } 
    default { "You entered No." } 
}
Sign up to request clarification or add additional context in comments.

5 Comments

can also do {$_ -in "y","yes"} but I'm not sure since which PS version.
-eq is case insensitive when performing string comparison so ToLower is not needed
Likewise, you can do { "y", "yes" -contains $_ } And this is also case-insensitive, so 'YES' and 'yeS' and so on also work.
The switch is also case-insensitive by default.
71

I found that this works and seems more readable:

switch($someString)
{
    { @("y", "yes") -contains $_ } { "You entered Yes." }
    default { "You entered No." }
}

The "-contains" operator performs a non-case sensitive search, so you don't need to use "ToLower()". If you do want it to be case sensitive, you can use "-ccontains" instead.

2 Comments

"-contains" does match case-insensitive, however it also will find the search string "yes" anywhere within $_. I generally stay away from -contains unless I need that kind of comparison, as it would potentially return TRUE if $someString contains the substring. Substring matching also requires more clock cycles.
@apraetor -contains won't match substrings (both "y", "yes" -contains "ye" and "PowerShell" -contains "Shell" returns False). You might be confused with -match? And switch default behavior also requires more clock cycles than switch -exact (so I think this solution is pretty good). learn.microsoft.com/en-us/powershell/module/…
55

You should be able to use a wildcard for your values:

switch -wildcard ($someString.ToLower())
{
    "y*" { "You entered Yes." }
    default { "You entered No." }
}

Regular expressions are also allowed.

switch -regex ($someString.ToLower())
{
    "y(es)?" { "You entered Yes." }
    default { "You entered No." }
}

PowerShell switch documentation: Using the Switch Statement

4 Comments

This is a great solution, although "technically" since I was asking to use separate values, I've marked fletcher as the answer.
Fair enough, although a different regular expression could probably do the same thing.
Regex approach would be more concise.
You don't actually need the ToLower() because equality of strings is case insensitive by default in PowerShell.
14
switch($someString.ToLower())
{
    "yes"   { $_ = "y" }
    "y"     { "You entered Yes." }
    default { "You entered No." }
}

You can arbitrarily branch, cascade, and merge cases in this fashion, as long as the target case is located below/after the case or cases where the $_ variable is respectively reassigned.


n.b. As cute as this behavior is, it seems to reveal that the PowerShell interpreter is not implementing switch/case as efficiently as one might hope or assume. For one, stepping with the ISE debugger suggests that instead of optimized lookup, hashing, or binary branching, each case is tested in turn, like so many if-else statements. (As such, consider putting your most common cases first.) Also, as shown in this answer, PowerShell continues testing cases after having satisfied one. And cruelly enough, there even happens to be a special optimized 'switch' opcode available in .NET CIL which, because of this behavior, PowerShell can't take advantage of.

4 Comments

It's not cute- it's documented. Add a break statement if you don't want subsequent branches evaluated.
@Frank Fair enough, "cute" may not have been the best word for referring to a keyword behavior that's altered or atypical vis-a-vis its historical semantics in numerous languages since 'C' (1975) or earlier.
@GlennSlayden well I mean it's literally more powerful, where the alternative for certain usecases falls back to exhaustive hard-coding. It's a pretty basic and unsurprising change people learning the language will just be aware of, as opposed to people just picking it up without trying to deliberately learn, relying solely on their past knowledge.
That's how terrible code is written, frankly, as opposed to being clean & idiomatic in new technologies. Those that are overly comfortable in older languages can stay there, right? If you're implying well-thought-out changes are in any way bad, considering @Frank said they are indeed documented.
12

A slight modification to derekerdmann's post to meet the original request using regex's alternation operator "|"(pipe).

It's also slightly easier for regex newbies to understand and read.

Note that while using regex, if you don't put the start of string character "^"(caret/circumflex) and/or end of string character "$"(dollar) then you may get unexpected/unintuitive behavior (like matching "yesterday" or "why").

Putting grouping characters "()"(parentheses) around the options reduces the need to put start and end of string characters for each option. Without them, you'll get possibly unexpected behavior if you're not savvy with regex. Of course, if you're not processing user input, but rather some set of known strings, it will be more readable without grouping and start and end of string characters.

switch -regex ($someString) #many have noted ToLower() here is redundant
{
        #processing user input
    "^(y|yes|indubitably)$" { "You entered Yes." }

        # not processing user input
    "y|yes|indubitably" { "Yes was the selected string" } 
    default { "You entered No." } 
}

Comments

9

Here's another one. Switch is case insensitive anyway. -eq with an array on the left will return the thing it's equal to, and anything returned is true.

switch($someString)
{
  { 'y', 'yes' -eq $_ } { 'You entered Yes.' }
  default               { 'You entered No.'  }
}

Comments

6

Supports entering y|ye|yes and case insensitive.

switch -regex ($someString.ToLower()) {
        "^y(es?)?$" {
            "You entered Yes." 
        }
        default { "You entered No." }
}

1 Comment

Actually, your expression "[yes]" matches any occurrence of the characters 'y', 'e', or 's' anywhere in $someString. Even if $someString is "no! no! no!s" that switch block will return "You entered Yes." because of the trailing 's'. To match y|ye|yes the expression should be "^y(es?)?$".
6

The answer given by js2010 has value, especially in the context of the question. But, in the context of people searching for examples of how to use the Switch statement, it could use an explanation of why it works, and a couple of potential problems.

The -eq operator, with an array on the left, will filter out only items that match the single value given on the right. If a single value is returned, then the results is a single value, but if more than one value is returned, then the results is an array.

The Switch statement creates a context where a Boolean value is expected, and, in that context, the results of the -eq comparison is determined to be $false if it is '', "", $null, 0, or 0.0 in value, else, it is considered to be $true. This means that testing for an empty string will, as in the following example, fail. The following example also shows how to do a case sensitive test using -ceq, and shows the sometimes useful behavior of Switch when the break statement isn't used:

foreach ($someString in '', 'C', 'd', 'E') {
    switch($someString) {
        { '', 'b', 'c', 'd', 'g' -eq $_ }   { "-eq: '$_' does Match" }
        { '', 'b', 'c', 'd', 'g' -ceq $_ }  { "-ceq '$_' does Match" }
        default                 { "'$_' doesn't Match" }
    }
}

As you seen in the following example results, even though '' is in the list of items to check for, the result of -eq is a single item of '', which is converted to the Boolean value $false and results in '' being treated as if it isn't in the list.

'' doesn't Match
-eq: 'C' does Match
-eq: 'd' does Match
-ceq 'd' does Match
'E' doesn't Match

The other problem, as in the following example, is when you place the single test value on the left, and the array of items to test against on right.

foreach ($someString in '', 'C', 'd', 'E') {
    switch($someString) {
        { $_ -eq '', 'b', 'c', 'd', 'g' }   { "-eq: '$_' does Match" }
        { $_ -ceq '', 'b', 'c', 'd', 'g' }  { "-ceq '$_' does Match" }
        default                 { "'$_' doesn't Match" }
    }
}

As you can see in the following example results, nothing matches:

'' doesn't Match   
'C' doesn't Match  
'd' doesn't Match  
'E' doesn't Match

These problems are solved by always having the array of items to test against on the left side of the -eq operator, and by having -eq return an array of 2 empty strings when testing for an empty string. The array of empty strings returned by -eq is a non $null value, and is converted to $true.

foreach ($someString in '', 'C', 'd', 'E') {
    switch($someString) {
        { '', '', 'b', 'c', 'd', 'g' -eq $_ }   { "-eq: '$_' does Match" }
        { '', '', 'b', 'c', 'd', 'g' -ceq $_ }  { "-ceq '$_' does Match" }
        default                 { "'$_' doesn't Match" }
    }
}

As you can see in the following example, having -eq and -ceq return an array of empty strings, which is then converted Boolean $true, will correctly match '':

-eq: '' does Match
-ceq '' does Match
-eq: 'C' does Match
-eq: 'd' does Match
-ceq 'd' does Match
'E' doesn't Match

If you aim to use this method with any value that PowerShell evaluates to $false, you should consider the following:

  1. The -eq operator considers these the same: '' and ""
  2. The -eq operator considers these the same: $false, 0, and 0.0.
  3. If you have a variable assigned an empty array, as in $emptyArray = @(), then -eq operator considers these the same: $null and $emptyArray.

Comments

0

In a simplier form, can also do this:

switch($someString.ToLower()) 
{ 
  {'y|yes'} { "You entered Yes." } 
  default { "You entered No." } 
}

1 Comment

no, you can not
-3

The switch doesn't appear to be case sensitive in PowerShell 5.1. All four of the $someString examples below work. [Fixed, thanks js2010]

$someString = "YES"
$someString = "yes"
$someString = "yEs"
$someString = "y"
$someString = "n"

switch ($someString) {
   {"y","yes" -eq $_} { "You entered Yes." }
   Default { "You didn't enter Yes."}
}

2 Comments

This is a wrong answer. Correct could be like: $a = 'b'; switch ($a) { {$_ -in 'a', 'b'} {'bingo'}; default {'nope!'} }
Nice try, but {"y","yes"} would be true for any answer. Another alternative: {"y","yes" -eq $_}
-5

After searching a solution for the same problem like you, I've found this small topic here. In advance I got a much smoother solution for this switch, case statement

switch($someString) #switch is caseINsensitive, so you don't need to lower
{
    { 'y' -or 'yes' } { "You entered Yes." }
    default { "You entered No." }
}

1 Comment

{'y' -or 'yes'} always evaluates to true so the default branch is never hit

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.