与许多其他语言一样,PowerShell有用于控制脚本内执行流程的命令。其中一个陈述是 转变 statement and in PowerShell, it offers features that are not found in other languages. Today we will take a deep dive into working with the PowerShell 转变.

指数

如果声明

One of the first statements that you will learn is the if statement. It lets you execute a script block if a statement is $true.

    if ( Test-Path $Path )
    {
        Remove-Item $Path
    }

You can have much more complicated logic by using elseif and else statements. Here is an example where I have a numeric value for day of the week and I want to get the name as a string.

    $day = 3

    if ( $day -eq 0 ) { $result = 'Sunday'        }
    elseif ( $day -eq 1 ) { $result = 'Monday'    }
    elseif ( $day -eq 2 ) { $result = 'Tuesday'   }
    elseif ( $day -eq 3 ) { $result = 'Wednesday' }
    elseif ( $day -eq 4 ) { $result = 'Thursday'  }
    elseif ( $day -eq 5 ) { $result = 'Friday'    }
    elseif ( $day -eq 6 ) { $result = 'Saturday'  }

    $result

    # Output
    Wednesday

It turns out that this is a very common pattern and there are a lot of ways to deal with this. One of them is with a 转变.

切换语句

The 转变 statement allows you to provide a variable and a list of possible values. If the value matches the variable, then it’s scriptblock will be executed.

    $day = 3

    转变 ( $day )
    {
        0 { $result = 'Sunday'    }
        1 { $result = 'Monday'    }
        2 { $result = 'Tuesday'   }
        3 { $result = 'Wednesday' }
        4 { $result = 'Thursday'  }
        5 { $result = 'Friday'    }
        6 { $result = 'Saturday'  }
    }

    $result

    # Output
    'Wednesday'

For this example, the value of $day matches one of the numeric values, then the correct name will be assigned to $result. We are only doing a variable assignment in this example, but any PowerShell can be executed in those script blocks.

分配给变量

我们可以以另一种方式写下这个榜样。

    $result = 转变 ( $day )
    {
        0 { 'Sunday'    }
        1 { 'Monday'    }
        2 { 'Tuesday'   }
        3 { 'Wednesday' }
        4 { 'Thursday'  }
        5 { 'Friday'    }
        6 { 'Saturday'  }
    }

We are placing the value on the PowerShell pipeline and assigning it to the $result. You can do this same thing with the if and foreach statements.

默认

We can use the default keyword to identify the what should happen if there is no match.

    $result = 转变 ( $day )
    {
        0 { 'Sunday' }
        # ...
        6 { 'Saturday' }
        default { 'Unknown' }
    }

Here we return the value Unknown in the default case.

我在最后一个例子中匹配数字,但您也可以匹配字符串。

    $item = 'Role'

    转变 ( $item )
    {
        Component
        {
            'is a component'
        }
        Role
        {
            'is a role'
        }
        Location
        {
            'is a location'
        }
    }

    # Output
    is a role

I decided not to wrap the Component,Role and Location matches in quotes here to hilight that they are optional. The 转变 treats those as a string in most cases.

阵列

One of the cool features of the PowerShell 转变 is the way it handles arrays. If you give a 转变 an array, it will process each element in that collection.

    $roles = @('WEB','Database')

    转变 ( $roles ) {
        'Database'   { 'Configure SQL' }
        'WEB'        { 'Configure IIS' }
        'FileServer' { 'Configure Share' }
    }

    # Output
    Configure IIS
    Configure SQL

如果您的数组中有重复的项目,那么它们将通过相应的部分多次匹配。

Psitem.

You can use the $PSItem or $_ to reference the current item that was processed. When we do a simple match, $PSItem will be the value that we are matching. I will be performing some advanced matches in the next section where this will be used.

参数

A unique feature of the PowerShell 转变 is that it has a number of 开关参数 这改变了它的表现。

-区分大小写

The matches are not case sensitive by default. If you need to be case sensitive then you can use -区分大小写. This can be used in combination with the other switch parameters.

- WildCard.

We can enable wildcard support with the -wildcard switch. This uses the same wildcard logic as the -like operator to do each match.

    $Message = 'Warning, out of disk space'

    转变  -  WildCard. ( $message )
    {
        'Error*'
        {
            Write-Error -Message $Message
        }
        'Warning*'
        {
            Write-Warning -Message $Message
        }
        default
        {
            Write-Information $message
        }
    }

    # Output
    WARNING: Warning, out of disk space

在这里,我们正在处理消息,然后根据内容在不同的流上输出它。

-regex.

Switch语句支持正则表达式匹配,就像它是通配符。

    转变 -regex. ( $message )
    {
        '^Error'
        {
            Write-Error -Message $Message
        }
        '^Warning'
        {
            Write-Warning -Message $Message
        }
        default
        {
            Write-Information $message
        }
    }

我有更多在我写的另一篇文章中使用正则表达式的例子: 使用正则表达式的许多方法.

-文件

A little known feature of the switch statement is that it can process a file with the -文件 parameter. You use -file with a path to a file instead of giving it a variable expression.

    转变  -  WildCard. -文件 $path
    {
        'Error*'
        {
            Write-Error -Message $PSItem
        }
        'Warning*'
        {
            Write-Warning -Message $PSItem
        }
        default
        {
            Write-Output $PSItem
        }
    }

It works just like processing an array. In this example, I combine it with wildcard matching and make use of the $PSItem. This would process a log file and convert it to warning and error messages depending on the regex matches.

高级细节

现在您了解所有这些记录的功能,我们可以在更高级处理的上下文中使用它们。

表达式

The 转变 can be on an expression instead of a variable.

    转变 ( ( Get-Service | Where status -eq 'running' ).name ) {...}

无论表达如何评估将是用于匹配的值。

多场比赛

You may have already picked up on this, but a 转变 can match to multiple conditions. This is especially true when using -wildcard or -regex matches. Be aware that you can add the same condition multiple times and all of them will trigger.

    转变 ( 'Word' )
    {
        'word' { 'lower case word match' }
        'Word' { 'mixed case word match' }
        'WORD' { 'upper case word match' }
    }

    # Output
    lower case word match
    mixed case word match
    upper case word match

这些陈述中的三个都将射击。这表明检查每个条件(按顺序)。这对于处理阵列来保持每个项目将检查每个条件的阵列。

继续

Normally, this is where I would introduce the break statement, but it is better that we learn how to use continue first. Just like with a foreach loop, continue will continue onto the next item in the collection or exit the 转变 if there are no more items. We can rewrite that last example with continue statements so that only one statement executes.

    转变 ( 'Word' )
    {
        'word'
        {
            'lower case word match'
            continue
        }
        'Word'
        {
            'mixed case word match'
            continue
        }
        'WORD'
        {
            'upper case word match'
            continue
        }
    }

    # Output
    lower case word match

第一个是匹配的,而不是匹配所有三个项目,并且开关继续到下一个值。因为没有剩余值来处理,因此开关退出。下一个例子显示了通配符如何匹配多个项目。

    转变  -  WildCard. -文件 $path
    {
        '*Error*'
        {
            Write-Error -Message $PSItem
            continue
        }
        '*Warning*'
        {
            Write-Warning -Message $PSItem
            continue
        }
        default
        {
            Write-Output $PSItem
        }
    }

Because a line in the input file could contain both the word Error and Warning, we only want the first one to execute and then continue processing the file.

休息

A break statement will exit the switch. This is the same behavior that continue will present for single values. The big difference is when processing an array. break will stop all processing in the switch and continue will move onto the next item.

    $Messages = @(
        'Downloading update'
        'Ran into errors downloading file'
        'Error: out of disk space'
        'Sending email'
        '...'
    )

    转变  -  WildCard. ($Messages)
    {
        'Error*'
        {
            Write-Error -Message $PSItem
            break
        }
        '*Error*'
        {
            Write-Warning -Message $PSItem
            continue
        }
        '*Warning*'
        {
            Write-Warning -Message $PSItem
            continue
        }
        default
        {
            Write-Output $PSItem
        }
    }

    # Output
    Downloading update
    WARNING: Ran into errors downloading file
    write-error -message $PSItem : Error: out of disk space
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException

In this case, if we hit any lines that start with Error then we will get an error and the switch will stop. This is what that break statement is doing for us. If we find Error inside the string and not just at the beginning, we will write it as a warning. We will do the same thing for Warning. It is possible that a line could have both the word Error and Warning, but we only need one to process. This is what the continue statement is doing for us.

打破标签

The 转变 statement supports break/continue labels just like foreach.

    :filelist foreach($path in $logs)
    {
        :logFile 转变  -  WildCard. -文件 $path
        {
            'Error*'
            {
                Write-Error -Message $PSItem
                break filelist
            }
            'Warning*'
            {
                Write-Error -Message $PSItem
                break logFile
            }
            default
            {
                Write-Output $PSItem
            }
        }
    }

I personally don’t like the use of break labels but I wanted to point them out because they are confusing if you have never seen them before. When you have multiple 转变 or foreach statements that are nested, you may want to break out of more than the inner most item. You can place a label on a 转变 that can be the target of your break.

枚举

电源外壳 5.0给了我们枚举,我们可以在交换机中使用它们。

    enum Context {
        Component
        Role
        Location
    }

    $item = [Context]::Role

    转变 ( $item )
    {
        Component
        {
            'is a component'
        }
        Role
        {
            'is a role'
        }
        Location
        {
            'is a location'
        }
    }

    # Output
    is a role

如果您想将所有内容保存为强类型枚举,那么您可以将它们放入括号中。

    转变 ($item )
    {
        ([Context]::Component)
        {
            'is a component'
        }
        ([Context]::Role)
        {
            'is a role'
        }
        ([Context]::Location)
        {
            'is a location'
        }
    }

The parentheses are needed here so that the switch does not treat the value [Context]::Location as a literal string.

scriptblock.

如果需要,我们可以使用ScriptBlock执行匹配的评估。

    $age = 37

    转变 ( $age )
    {
        {$PSItem -le 18}
        {
            'child'
        }
        {$PSItem -gt 18}
        {
            'adult'
        }
    }

    # Output
    'adult'

This adds a lot of complexity and can make your 转变 hard to read. In most cases where you would use something like this it would be better to use if and elseif statements. I would consider using this if I already had a large switch in place and I needed 2 items to hit the same evaluation block.

我认为有粘性有所帮助的一件事是将Scriptlock放在括号中。

    转变 ( $age )
    {
        ({$PSItem -le 18})
        {
            'child'
        }
        ({$PSItem -gt 18})
        {
            'adult'
        }
    }

它仍然以相同的方式执行,并在快速查看它时给出更好的视觉休息。

正则表达式$比赛

We need to revisit regex to touch on something that is not immediately obvious. The use of regex populates the $matches variable. I do go into the use of $matches more when I talk about 使用正则表达式的许多方法。这是一个快速样本,以便以命名的匹配方式显示它。

    $message = 'my ssn is 123-23-3456 and credit card: 1234-5678-1234-5678'

    转变 -regex ($message)
    {
        '(?<SSN>\d\d\d-\d\d-\d\d\d\d)'
        {
            Write-Warning "message contains a SSN: $($matches.SSN)"
        }
        '(?<CC>\d\d\d\d-\d\d\d\d-\d\d\d\d-\d\d\d\d)'
        {
            Write-Warning "message contains a credit card number: $($matches.CC)"
        }
        '(?<Phone>\d\d\d-\d\d\d-\d\d\d\d)'
        {
            Write-Warning "message contains a phone number: $($matches.Phone)"
        }
    }

    # Output
    WARNING: message may contain a SSN: 123-23-3456
    WARNING: message may contain a credit card number: 1234-5678-1234-5678

$ null.

You can match a $ null. value that does not have to be the default.

    $value = $ null.

    转变 ( $value )
    {
        $ null.
        {
            'Value is null'
        }
        default
        {
            'value is not null'
        }
    }

    # Output
    Value is null

一个空字符串也是如此。

    转变 ( '' )
    {
        ''
        {
            'Value is empty'
        }
        default
        {
            'value is a empty string'
        }
    }

    # Output
    Value is empty

常数表达

Lee Dailey pointed out that we can use a constant $true expression to evaluate [bool] items. Imagine if we have a lot of boolean checks that need to happen.

    $isVisible = $false
    $isEnabled = $true
    $isSecure = $true

    转变 ( $true )
    {
        $isEnabled
        {
            'Do-Action'
        }
        $isVisible
        {
            'Show-Animation'
        }
        $isSecure
        {
            'Enable-AdminMenu'
        }
    }

    # Output
    Do-Action
    Enabled-AdminMenu

这是一种非常干净的方式来评估和采取几个布尔字段状态的行动。关于此的很酷的事情是您可以将一个匹配翻转尚未评估的值的状态。

    $isVisible = $false
    $isEnabled = $true
    $isAdmin = $false

    转变 ( $true )
    {
        $isEnabled
        {
            'Do-Action'
            $isVisible = $true
        }
        $isVisible
        {
            'Show-Animation'
        }
        $isAdmin
        {
            'Enable-AdminMenu'
        }
    }

    # Output
    Do-Action
    Show-Animation

Setting $isEnabled to $true in this example will make sure the $isVisible is also set to $true. Then when the $isVisible gets evaluated, its scriptblock will be invoked. This is a bit counter-intuitive but is a very clever use of the mechanics.

$ Switch自动变量

When the 转变 is processing its values, it creates an enumerator and calls it $switch. This is an automatic variable created by PowerShell and you have the option to manipulate it directly.

这对我来说是指出的 / U / Frmadsen

这将为您提供以下结果:

    2
    4

By moving the enumerator forward, the next item will not get processed by the 转变 but you can access that value directly. I would call it madness.

其他模式

Hashtables.

我最受欢迎的帖子之一是我所做的那个 你想要了解的一切有关Hashtables. One of the example use-cases for a hashtable is to be a lookup table. That is an alternate approach to a common pattern that a 转变 statement is often addressing.

    $day = 3

    $lookup = @{
        0 = 'Sunday'
        1 = 'Monday'
        2 = 'Tuesday'
        3 = 'Wednesday'
        4 = 'Thursday'
        5 = 'Friday'
        6 = 'Saturday'
    }

    $lookup[$day]

    # Output
    Wednesday

If I am only using a 转变 as a lookup, I will quite often use a hashtable instead.

枚举

电源外壳 5.0 introduced the 枚举 and it is also an option in this case.

    $day = 3

    enum DayOfTheWeek {
        Sunday
        Monday
        Tuesday
        Wednesday
        Thursday
        Friday
        Saturday
    }

    [DayOfTheWeek]$day

    # Output
    Wednesday

我们可以整天看看不同的方法来解决这个问题。我只是想确保你知道你有选择。

最后的话

Switch语句在表面上很简单,但它提供了一些高级功能,大多数人都没有实现。在需要时,将这些功能一起使其成为一个非常强大的功能。我希望你学到了你以前没有意识的事情。