简体   繁体   中英

What's the PowerShell syntax for multiple values in a switch statement?

I basically want to do this:

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

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.

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

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.


nb 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. (If so, 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.

Supports entering y|ye|yes and case insensitive.

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

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." } 
}

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.'  }
}

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" }
        { $_ -eq '', '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

This 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

The switch doesn't appear to be case sensitive in PowerShell 5.1. All four of the $someString examples below work. [ This is not correct -- js2010]

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

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

Here is my $PSVersionTable data.

Name                           Value
----                           -----
PSVersion                      5.1.17763.771
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17763.771
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

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." }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM