社内SEの話

日々起きたことの記録用

【PowerShell】SWITCH文のすべて

はじめに

If文が複雑化する時によく使われるのが、Switch文です。

すでに他言語を使ったことのある人であれば動作の説明が不要ですが、PowerShellの場合は通常の使い方の他に色々な使い方ができる面白い言語です。

Switchの使い方についてまとめましたので、もしよければ参考にしてみてください。

基本的な使い方

$num = 10
switch ($num) {
    10 {
        write-host "10です"
    }   
    Default {
        Write-host "10以外です"
    }
}

条件に変数を使う

$num = 10
$a = 5
$b = 10
$c = 15
switch ($num) {
    $a {
        write-host "$a です"
    }
    $b {
        write-host "$b です"
    }
    $c {
        write-host "$c です"
    }
    Default {
        Write-host "以外です"
    }
}

条件が動的に変わる場合に使えます

結果を変数に代入する

$num = 10
$a = 5
$b = 10
$result =switch ($num) {
    $a {
        5
    }
    $b {
        10
    }
    Default {
        Write-host "以外です"
    }
}
$result

分岐の数だけ変数を記述しなくても大丈夫です。

文字列の場合

$str = 'hoge'
$str2 = "hogera"
switch ($str) {
    hoge {
        Write-Host "'で囲わなくても大丈夫"
    }
    'fuga' {
        Write-Host "'で囲っても大丈夫"
    }
    "piyo" {
        Write-Host """で囲っても大丈夫"
    }
    $str2 {
        Write-Host "変数でも大丈夫"
    }
    Default {
        Write-host "以外です"
    }
}

コーディングルールによって、記述すればいいと思います。

配列を渡した場合

$str = @('hoge','fuga')
switch ($str) {
    hoge {
        Write-Host "hoge"
    }
    fuga {
        Write-Host "fuga"
    }
    Default {
        Write-host "以外です"
    }
}

結果

hoge
fuga

自動でループ処理がされます。

あいまい一致させる(ワイルドカード)

$str = @('今日は10年に一度の大雪の日です')
switch -Wildcard ($str) {
    "今日は*" {
        Write-Host "いい天気でした"
    }
    "*大雨*"{
        Write-Host "傘が必要でした"
    }
    Default {
        Write-host "以外です"
    }
}

switch -Wildcard (<変数>)で条件を曖昧一致できます。

正規表現を使う

$str = @('2023-04-01')
switch -Regex ($str) {
    "^d{4}" {
        Write-Host "正規表現で一致"
    }
    Default {
        Write-host "以外です"
    }
}

switch -Regex (<変数>)で条件で正規表現が使えます。

スクリプトブロックを使う

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

Powershellでは{}で括られるとその中だけで処理するスクリプトブロックという機能があります。

スクリプトブロックを使って、条件式を設定する方法です。

if文の代用として使えますが、可読性が悪くなる可能性があるので注意が必要です。

()で囲む

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

でも動作しますが、{}が多くなってしまいどこが条件式かわからなくならないように条件式は()で囲みます。

とは言え簡単なif文では表せない時に非常に重宝する書き方になります。

可読性を担保させる書き方であれば十分実用的かと思います。

Continue、Breakを使う

PowershellのSwitchはループ処理に似た動作をします。そのためContinueとBreakが備わっています。

動作に違いがあるで解説します

switch ( 'Hoge' )
{
    'hoge'
    {
        '全部小文字'
    }
    'Hoge'
    {
        '頭文字だけ大文字'
    }
    'HOGE'
    {
        '全部大文字'
    }
}

結果は

全部小文字
頭文字だけ大文字
全部大文字

全部の条件に一致します。

条件判断に大文字小文字で判断していないために起こる現象です。もし小文字大文字を判断したい場合は、正規表現を使いましょう

Continueを使った場合

switch ( 'Hoge' )
{
    'hoge'
    {
        '全部小文字'
        Continue
    }
    'Hoge'
    {
        '頭文字だけ大文字'
        Continue
    }
    'HOGE'
    {
        '全部大文字'
        Continue
    }
}

結果

全部小文字

Breakを使った場合

switch ( 'Hoge' )
{
    'hoge'
    {
        '全部小文字'
        Break
    }
    'Hoge'
    {
        '頭文字だけ大文字'
        Break
    }
    'HOGE'
    {
        '全部大文字'
        Break
    }
}

結果

全部小文字

どちらも1つ目の条件で終了しています。

途中で処理を中断させるのはどちらも一緒です。

違うのは値が配列で渡された際に動作が変わります。

Continueの場合

switch (@('Hoge','hoge') )
{
    'hoge'
    {
        '全部小文字'
        Continue
    }
    'Hoge'
    {
        '頭文字だけ大文字'
        Continue
    }
    'HOGE'
    {
        '全部大文字'
        Continue
    }
}

結果

全部小文字
全部小文字

Breakの場合

switch (@('Hoge','hoge') )
{
    'hoge'
    {
        '全部小文字'
        Break
    }
    'Hoge'
    {
        '頭文字だけ大文字'
        Break
    }
    'HOGE'
    {
        '全部大文字'
        Break
    }
}

結果

全部小文字

Continueはループを続けるのに対して、Breakはループも止めてしまいます。

動作に違いがあるので、実際に使う際にどちらを使うか判断してください

ラベルを使う

前述のContinueとBreakはループ処理でも使用できます。そのためループ処理の中にSwitch文を入れた時にContinueやBreakを使ってしまうと、思った動作がしないことが考えられます。

どのContinueなど明示的に指定できるのがラベル機能です。

例が若干悪いですが、一致したら処理をすべて停止させたい時にBreakを使うとします

foreach($f in @(@('Hoge','hoge'))){
    switch ($f){
        'hoge'
        {
            '全部小文字で一回だけ表示してほしい'
            break
        }
        'Hoge'
        {
            '頭文字だけ大文字'
            break
        }
        'HOGE'
        {
            '全部大文字'
            break
        }
    }
}

結果

全部小文字で一回だけ表示してほしい
全部小文字で一回だけ表示してほしい

2回表示してしまいます。

これはBreakがSwitchの中で作用してしまい、ForeachのBreakで作用しないために起こってしまいます。

よく使う事例だけに、注意が必要です。

そこでラベルを使います。

書き方は ループ文やSwitch文の前に :ラベル名 を記述します

そしてContinueやBreakの後ろに ラベル名を記述します。

:ArrayLabel foreach($f in @(@('Hoge','hoge'))){
    :SwitchLabel switch ($f){
        'hoge'
        {
            '全部小文字で一回だけ表示してほしい'
            break ArrayLabel
        }
        'Hoge'
        {
            '頭文字だけ大文字'
            break ArrayLabel
        }
        'HOGE'
        {
            '全部大文字'
            break ArrayLabel
        }
    }
}

結果

全部小文字で一回だけ表示してほしい

一回だけの処理ができました。

ただこれも複雑化しやすいので、入れ子に注意しつつ可読性に注意したほうがいいと思います。

docs.microsoft.com

docs.microsoft.com

learn.microsoft.com