次の方法で共有


switch ステートメントについて知りたかったすべてのこと

他の多くの言語と同様に、PowerShell にはスクリプト内の実行フローを制御するためのコマンドがあります。 これらのステートメントの 1 つは switch ステートメントであり、PowerShell では、他の言語では見つからない機能を提供します。 現在、PowerShell switchの使用について詳しく説明します。

この記事の 元のバージョン は、 @KevinMarquetteによって書かれたブログに掲載されました。 PowerShell チームは、このコンテンツを Microsoft と共有してくれた Kevin に感謝します。 PowerShellExplained.com で彼のブログをチェックしてください。

if ステートメント

最初に学習するステートメントの 1 つは、 if ステートメントです。 ステートメントが $trueされている場合は、スクリプト ブロックを実行できます。

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

elseifステートメントとelseステートメントを使用すると、はるかに複雑なロジックを使用できます。 曜日の数値があり、名前を文字列として取得する例を次に示します。

$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
Wednesday

これは一般的なパターンであり、これに対処する多くの方法があることが判明しました。 そのうちの 1 つは switchです。

Switch ステートメント

switch ステートメントを使用すると、変数と使用可能な値の一覧を指定できます。 値が変数と一致する場合、そのステートメント ブロックが実行されます。

$day = 3

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

$result
'Wednesday'

この例では、 $day の値がいずれかの数値と一致し、正しい名前が $resultに割り当てられます。 この例では変数の割り当てを行うだけですが、これらのスクリプト ブロックでは任意の PowerShell を実行できます。

変数に割り当てる

この最後の例は別の方法で記述できます。

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

PowerShell パイプラインに値を配置し、 $resultに割り当てます。 これは、 if ステートメントと foreach ステートメントでも同じことができます。

既定値

defaultキーワードを使用して、一致するものがない場合に何が起こるかを特定できます。

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

ここでは、既定のケースで Unknown 値を返します。

ストリングス

私は最後の例で数字を一致していましたが、文字列を一致させることもできます。

$item = 'Role'

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

ここでは、 ComponentRoleLocation の一致を引用符で囲まず、省略可能であることを強調することにしました。 switchでは、ほとんどの場合、文字列として扱われます。

配列

PowerShell switch のクールな機能の 1 つは、配列を処理する方法です。 switch配列を指定すると、そのコレクション内の各要素が処理されます。

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

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

配列内に項目が繰り返し存在する場合、それらは適切なセクションによって複数回照合されます。

PSItem

$PSItemまたは$_を使用して、処理された現在のアイテムを参照できます。 単純な一致を行う場合、 $PSItem は一致する値です。 この変数を使用する次のセクションでは、高度な一致をいくつか実行します。

パラメーター

PowerShell switch の固有の機能は、動作を変更するスイッチ パラメーターが多数あるということです。

-CaseSensitive(大文字と小文字の区別)

既定では、一致は大文字と小文字を区別しません。 大文字と小文字を区別する必要がある場合は、 -CaseSensitiveを使用できます。 これは、他のスイッチ パラメーターと組み合わせて使用できます。

-ワイルドカード

-Wildcard スイッチでワイルドカードのサポートを有効にすることができます。 これにより、各一致を行うために、 -like 演算子と同じワイルドカード ロジックが使用されます。

$Message = 'Warning, out of disk space'

switch -Wildcard ( $message )
{
    'Error*'
    {
        Write-Error -Message $Message
    }
    'Warning*'
    {
        Write-Warning -Message $Message
    }
    default
    {
        Write-Information $message
    }
}
WARNING: Warning, out of disk space

ここでは、メッセージを処理し、内容に基づいて異なるストリームに出力します。

-Regex

switch ステートメントでは、ワイルドカードと同様に正規表現の一致がサポートされます。

switch -Regex ( $message )
{
    '^Error'
    {
        Write-Error -Message $Message
    }
    '^Warning'
    {
        Write-Warning -Message $Message
    }
    default
    {
        Write-Information $message
    }
}

私が書いた別の記事で正規表現を使用するより多くの例があります: 正規表現を使用する多くの方法

-File

switch ステートメントのあまり知られていない機能は、 -File パラメーターを使用してファイルを処理できることです。 -Fileは、変数式を指定する代わりに、ファイルへのパスと共に使用します。

switch -Wildcard -File $path
{
    'Error*'
    {
        Write-Error -Message $PSItem
    }
    'Warning*'
    {
        Write-Warning -Message $PSItem
    }
    default
    {
        Write-Output $PSItem
    }
}

配列の処理と同じように機能します。 この例では、ワイルドカード マッチングと組み合わせて、 $PSItemを使用します。 これにより、ログ ファイルが処理され、正規表現の一致に応じて警告メッセージとエラー メッセージに変換されます。

詳細な情報

これらのドキュメントに記載されているすべての機能を理解したので、より高度な処理のコンテキストで使用できます。

Expressions

switchは、変数ではなく式に対して指定できます。

switch ( ( Get-Service | where Status -EQ 'running' ).Name ) {...}

式の評価結果が、一致に使用される値です。

複数の一致

既にこれを取得している可能性がありますが、 switch は複数の条件に一致する可能性があります。 これは、 -Wildcard または -Regex の一致を使用する場合に特に当てはまります。 同じ条件を複数回追加でき、すべてがトリガーされます。

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

これらの 3 つのステートメントはすべて実行されます。 これは、すべての条件が (順番に) チェックされていることを示しています。 これは、各項目が各条件をチェックする配列を処理する場合に当てはまります。

続行

通常、ここで break ステートメントを紹介しますが、最初に continue の使用方法を学習することをお勧めします。 foreach ループと同様に、continueコレクション内の次の項目に進むか、項目がなくなった場合はswitchを終了します。 その最後の例を continue ステートメントで書き直して、1 つのステートメントのみが実行されるようにすることができます。

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

3 つの項目すべてを照合する代わりに、最初の項目が照合され、スイッチは次の値に進みます。 処理する値が残っていないため、スイッチは終了します。 次の例では、ワイルドカードが複数の項目と一致する方法を示します。

switch -Wildcard -File $path
{
    '*Error*'
    {
        Write-Error -Message $PSItem
        continue
    }
    '*Warning*'
    {
        Write-Warning -Message $PSItem
        continue
    }
    default
    {
        Write-Output $PSItem
    }
}

入力ファイル内の行には ErrorWarning の両方の単語を含めることができるため、最初の行のみを実行してから、ファイルの処理を続行します。

中断

break ステートメントがスイッチを終了します。 これは、単一の値に対してcontinueが示すのと同じ動作です。 この違いは、配列を処理するときに表示されます。 break はスイッチ内のすべての処理を停止し、 continue 次の項目に移動します。

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

switch -Wildcard ($Messages)
{
    'Error*'
    {
        Write-Error -Message $PSItem
        break
    }
    '*Error*'
    {
        Write-Warning -Message $PSItem
        continue
    }
    '*Warning*'
    {
        Write-Warning -Message $PSItem
        continue
    }
    default
    {
        Write-Output $PSItem
    }
}
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

この場合、 Error で始まる行にヒットすると、エラーが発生し、スイッチが停止します。 これは、 break ステートメントが私たちのために行っていることです。 最初だけでなく文字列内 Error が見つかると、警告として書き込みます。 Warningに対して同じことを行います。 1 行に ErrorWarning の両方の単語を含めることができる可能性がありますが、処理する必要があるのは 1 つだけです。 これは、 continue ステートメントが行っていることです。

ラベルを区切る

switch ステートメントは、break/continueと同様にforeachラベルをサポートします。

:filelist foreach($path in $logs)
{
    :logFile switch -Wildcard -File $path
    {
        'Error*'
        {
            Write-Error -Message $PSItem
            break filelist
        }
        'Warning*'
        {
            Write-Error -Message $PSItem
            break logFile
        }
        default
        {
            Write-Output $PSItem
        }
    }
}

私は個人的にブレークラベルの使用は好きではありませんが、見たことがなければ分かりづらいかもしれないので、指摘したかったのです。 入れ子になった複数の switch または foreach ステートメントがある場合は、最内層の項目だけでなく、それ以上の階層からも脱出する必要があるかもしれません。 switchのターゲットとなるbreakにラベルを配置できます。

Enum

PowerShell 5.0 では列挙型が提供され、スイッチで使用できます。

enum Context {
    Component
    Role
    Location
}

$item = [Context]::Role

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

すべてを強く型付けされた列挙型として保持したい場合は、括弧で括ることができます。

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

ここでは、値 [Context]::Location をリテラル文字列として扱わないよう、かっこが必要です。

ScriptBlock

必要に応じて、scriptblock を使用して一致の評価を実行できます。

$age = 37

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

これにより、複雑さが増し、 switch が読みにくくなることができます。 ほとんどの場合、このようなものを使用する場合は、 if ステートメントと elseif ステートメントを使用することをお勧めします。 既に大きなスイッチがあり、同じ評価ブロックにヒットするために2つの項目が必要な場合は、これを使用することを検討します。

私が読みやすさに役立つと思うことは、スクリプトブロックをかっこで囲むことです。

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

それでも同じ方法で実行され、素早く見たときの視覚的区切りが良くなります。

Regex $Matches

すぐには明らかではないものに触れるには、正規表現を見直す必要があります。 正規表現を使用すると、 $Matches 変数が設定されます。 正規表現を使用する$Matchesについて話すときは、の使用について詳しく説明します。 実際に動作する様子を示す簡単なサンプルを名前付き一致を使用して次に示します。

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

switch -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)"
    }
}
WARNING: message may contain a SSN: 123-23-3456
WARNING: message may contain a credit card number: 1234-5678-1234-5678

$null

既定値である必要のない $null 値と一致させることができます。

$values = '', 5, $null
switch ( $values )
{
    $null          { "Value '$_' is `$null" }
    { '' -eq $_ }  { "Value '$_' is an empty string" }
    default        { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null

switch ステートメントで空の文字列をテストする場合は、生の値''ではなく、この例に示すように比較ステートメントを使用することが重要です。 switch ステートメントでは、生の値''$nullと一致します。 例えば次が挙げられます。

$values = '', 5, $null
switch ( $values )
{
    $null          { "Value '$_' is `$null" }
    ''             { "Value '$_' is an empty string" }
    default        { "Value [$_] isn't an empty string or `$null" }
}
Value '' is an empty string
Value [5] isn't an empty string or $null
Value '' is $null
Value '' is an empty string

また、コマンドレットからの空の戻り値には注意してください。 出力のないコマンドレットまたはパイプラインは、 default ケースを含め、何も一致しない空の配列として扱われます。

$file = Get-ChildItem NonExistantFile*
switch ( $file )
{
    $null   { '$file is $null' }
    default { "`$file is type $($file.GetType().Name)" }
}
# No matches

定数式

Lee Dailey 氏は、定数 $true 式を使用して、 [bool] 項目を評価できることを指摘しました。 発生する必要があるブール値チェックが複数あるとします。

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

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

これは、いくつかのブール型フィールドの状態を評価してアクションを実行するためのクリーンな方法です。 これに関する良い点は、まだ評価されていない値の状態を 1 つの一致で反転できることです。

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

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

この例で $isEnabled$true に設定すると、 $isVisible$true に設定されます。 次に、 $isVisible が評価されると、そのステートメント ブロックが呼び出されます。 これは少し直感に反していますが、メカニズムを巧妙に利用しています。

$switch の自動変数

switchは、値を処理するときに列挙子を作成し、$switch呼び出します。 これは PowerShell によって作成された自動変数であり、直接操作できます。

$a = 1, 2, 3, 4

switch($a) {
    1 { [void]$switch.MoveNext(); $switch.Current }
    3 { [void]$switch.MoveNext(); $switch.Current }
}

これにより、次の結果が得られます。

2
4

列挙子を前方に移動すると、次の項目は switch によって処理されませんが、その値に直接アクセスできます。 私はそれを狂いと呼ぶでしょう。

その他のパターン

ハッシュテーブル

私の最も人気のある投稿の1つは、 ハッシュテーブルでやったことです。 hashtableのユース ケースの 1 つは、参照テーブルです。 これは、 switch ステートメントが頻繁に対処する一般的なパターンの代替アプローチです。

$day = 3

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

$lookup[$day]
Wednesday

ルックアップとして switch のみを使用している場合は、代わりに hashtable を使用することがよくあります。

Enum

PowerShell 5.0 では、 enum が導入されました。この場合もオプションです。

$day = 3

enum DayOfTheWeek {
    Sunday
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
}

[DayOfTheWeek]$day
Wednesday

私たちは、この問題を解決するためのさまざまな方法を見て一日中行くことができます。 私はあなたがあなたがオプションを持っていることを知っていることを確認したかった。

最後の単語

switch ステートメントは表面的には単純ですが、ほとんどのユーザーが利用できないことに気付かない高度な機能がいくつか提供されています。 これらの機能を組み合わせることで、非常に強力な機能になります。 今まで気づかなかったことを学んでほしいと思います。