はじめに

シェルスクリプトで「この値だったらこうする、あの値だったらああする」って複数の条件分岐を書くとき、if文をずらずら並べてると読みづらくなりますよね。そんなときに便利なのがcaseコマンドです。

caseは複数の値やパターンに対する処理を、見やすく簡潔に書けるシェルスクリプトの制御構文。メニュー選択やオプション解析、ファイルの種類判定など、いろんな場面で大活躍します。

caseとは

caseはシェルの組み込みコマンドで、変数の値がどのパターンにマッチするかを判定して、対応する処理を実行します。他のプログラミング言語でいうswitch文みたいなやつですね。

特徴は以下の通り:

  • ワイルドカード(*?[]など)が使える
  • 複数のパターンを|で並べられる
  • if-elif-elseより読みやすい
  • パターンマッチングが強力

基本構文

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
case 変数 in
  パターン1)
    コマンド1
    ;;
  パターン2)
    コマンド2
    ;;
  *)
    デフォルトのコマンド
    ;;
esac

構文のポイント:

  • case 変数 inで始まり、esacで終わる(caseの逆綴り)
  • 各パターンは)で終わる
  • 各ケースの終わりは;;で区切る
  • *は「それ以外全部」(デフォルトケース)
  • パターンにはワイルドカードが使える

主なパターン記法

パターン 説明
文字列 完全一致 yesno
* 任意の文字列 デフォルトケース
? 任意の1文字 a?b(axb、aybなど)
[...] 文字クラス [0-9][a-z]
パターン1|パターン2 複数パターン(OR) y|yes|Y|YES
[!...] 否定文字クラス [!0-9](数字以外)

使用例

1. 基本的な使い方

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash
read -p "続けますか? (yes/no): " answer

case $answer in
  yes)
    echo "続行します"
    ;;
  no)
    echo "終了します"
    ;;
  *)
    echo "yes か no で答えてください"
    ;;
esac

シンプルな条件分岐。ifだとif [ "$answer" = "yes" ]って書くところが、caseだとすっきり書けます。

2. 複数パターンのマッチング

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#!/bin/bash
read -p "続けますか? (y/n): " answer

case $answer in
  y|yes|Y|YES)
    echo "続行します"
    ;;
  n|no|N|NO)
    echo "終了します"
    ;;
  *)
    echo "無効な入力です"
    ;;
esac

|を使って複数のパターンをまとめて処理。大文字小文字どっちでも反応するようにできます。

3. ワイルドカードを使ったパターン

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
filename="test.txt"

case $filename in
  *.txt)
    echo "テキストファイルです"
    ;;
  *.jpg|*.png|*.gif)
    echo "画像ファイルです"
    ;;
  *.sh)
    echo "シェルスクリプトです"
    ;;
  *)
    echo "不明なファイル形式です"
    ;;
esac

ファイルの拡張子で処理を分けるときに便利。*で「何でもいい文字列」を表現できます。

4. 数値の範囲判定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
read -p "1〜10の数字を入力: " num

case $num in
  [1-3])
    echo "小さい数字です"
    ;;
  [4-7])
    echo "中くらいの数字です"
    ;;
  [8-9]|10)
    echo "大きい数字です"
    ;;
  *)
    echo "範囲外の値です"
    ;;
esac

[1-3]みたいに範囲指定ができます。ただし、1桁の数字にしか使えないので注意。

5. メニューシステム

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
echo "=== メニュー ==="
echo "1. ファイル一覧"
echo "2. ディレクトリ作成"
echo "3. 終了"
read -p "選択してください: " choice

case $choice in
  1)
    echo "ファイル一覧:"
    ls -l
    ;;
  2)
    read -p "ディレクトリ名: " dirname
    mkdir "$dirname"
    echo "$dirname を作成しました"
    ;;
  3)
    echo "終了します"
    exit 0
    ;;
  *)
    echo "無効な選択です"
    ;;
esac

メニュー形式のスクリプトでよく使うパターン。数字で選択肢を選ばせるときに読みやすいです。

6. コマンドライン引数の解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
case $1 in
  start)
    echo "サービスを開始します"
    ;;
  stop)
    echo "サービスを停止します"
    ;;
  restart)
    echo "サービスを再起動します"
    ;;
  status)
    echo "サービスの状態を確認します"
    ;;
  *)
    echo "使い方: $0 {start|stop|restart|status}"
    exit 1
    ;;
esac

引数で動作を切り替えるスクリプトの典型的なパターン。systemctlみたいなコマンドの作り方ですね。

7. 日付や曜日による処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
day=$(date +%A)

case $day in
  Monday)
    echo "月曜日です。今週も頑張りましょう!"
    ;;
  Friday)
    echo "金曜日です。もうすぐ週末!"
    ;;
  Saturday|Sunday)
    echo "週末です。ゆっくり休んでください"
    ;;
  *)
    echo "平日です。お疲れ様です"
    ;;
esac

曜日によって処理を変えるスクリプト。cronで定期実行するときとかに使えます。

8. 環境変数による設定切り替え

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
case $ENVIRONMENT in
  dev|development)
    echo "開発環境で実行します"
    DEBUG=true
    ;;
  stg|staging)
    echo "ステージング環境で実行します"
    DEBUG=false
    ;;
  prod|production)
    echo "本番環境で実行します"
    DEBUG=false
    ;;
  *)
    echo "環境が指定されていません"
    exit 1
    ;;
esac

環境変数で動作を切り替える。複数の環境名を受け付けられるのが便利。

9. ファイルの種類判定(詳細版)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
file="document.pdf"

case $file in
  *.tar.gz|*.tgz)
    echo "圧縮されたアーカイブファイル"
    ;;
  *.zip|*.rar|*.7z)
    echo "圧縮ファイル"
    ;;
  *.jpg|*.jpeg|*.png|*.gif|*.bmp)
    echo "画像ファイル"
    ;;
  *.mp4|*.avi|*.mov|*.mkv)
    echo "動画ファイル"
    ;;
  *.pdf)
    echo "PDFドキュメント"
    ;;
  *.doc|*.docx|*.txt)
    echo "文書ファイル"
    ;;
  *)
    echo "不明なファイル形式"
    ;;
esac

より実践的なファイル判定。拡張子が二重になってるやつ(.tar.gz)もちゃんと判定できます。

10. ユーザー入力の検証

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/bash
read -p "メールアドレスを入力: " email

case $email in
  *@*.*)
    echo "有効なメールアドレス形式です"
    ;;
  *)
    echo "メールアドレスの形式が正しくありません"
    ;;
esac

簡易的なメールアドレスの検証。完璧じゃないけど、基本的なチェックには使えます。

11. 複数条件の組み合わせ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#!/bin/bash
read -p "ファイル名を入力: " filename

case $filename in
  [Tt]est*.txt)
    echo "testで始まるテキストファイル"
    ;;
  [0-9]*.log)
    echo "数字で始まるログファイル"
    ;;
  backup_[0-9][0-9][0-9][0-9]*.tar.gz)
    echo "バックアップアーカイブ(年付き)"
    ;;
  *)
    echo "その他のファイル"
    ;;
esac

文字クラスとワイルドカードを組み合わせた複雑なパターン。命名規則に沿ったファイルを見分けられます。

12. オプションフラグの解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
while [ $# -gt 0 ]; do
  case $1 in
    -h|--help)
      echo "使い方: $0 [options]"
      echo "  -h, --help     ヘルプを表示"
      echo "  -v, --version  バージョンを表示"
      echo "  -d, --debug    デバッグモード"
      exit 0
      ;;
    -v|--version)
      echo "バージョン 1.0.0"
      exit 0
      ;;
    -d|--debug)
      echo "デバッグモードを有効化"
      DEBUG=true
      shift
      ;;
    -*)
      echo "不明なオプション: $1"
      exit 1
      ;;
    *)
      break
      ;;
  esac
done

コマンドラインオプションを解析する実践的な例。-h--helpの両方を受け付けられます。

Tips・注意点

パターンマッチの順序

caseは上から順番に評価されて、最初にマッチしたパターンだけが実行されます。広いパターン(*など)は最後に書きましょう。

1
2
3
4
5
6
7
8
case $value in
  *)  # これを最初に書くと...
    echo "全部ここにマッチしちゃう"
    ;;
  specific)
    echo "ここには絶対来ない"
    ;;
esac

クォートは不要

パターンマッチング部分では変数にクォートを付けなくてOKです。というか付けると逆にマッチしません。

1
2
3
4
5
6
7
8
9
# OK
case $var in
  pattern) echo "マッチ" ;;
esac

# NG - パターンとして認識されない
case "$var" in
  "pattern") echo "これはダメ" ;;
esac

;;と;;&の違い

通常は;;を使いますが、他にも終端記号があります:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
case $value in
  a)
    echo "aにマッチ"
    ;;   # ここで終了(通常)
  b)
    echo "bにマッチ"
    ;&   # 次のケースも必ず実行
  c)
    echo "cにマッチ(or bの次)"
    ;;&  # 次のパターンマッチも試す
  *)
    echo "その他"
    ;;
esac

でも実際;;しか使わないことがほとんどです。

数値の範囲判定の限界

[1-9]は1桁の数字しか判定できません。10以上の数値には使えないので注意。

1
2
3
4
5
6
# これは期待通りに動かない
case $num in
  [10-20])  # 1, 0, -, 2 のいずれか1文字にマッチ
    echo "10から20の範囲"
    ;;
esac

複雑な数値範囲判定はif文を使った方がいいです。

正規表現は使えない

caseはワイルドカードパターンであって、正規表現じゃありません。.*とか[0-9]+みたいな正規表現は使えないので注意。

1
2
3
4
5
6
# これは正規表現じゃなくてワイルドカード
case $var in
  [0-9]*)  # 数字で始まる任意の文字列
    echo "数字始まり"
    ;;
esac

実践的な使い方

インタラクティブなメニューシステム

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/bin/bash

show_menu() {
  echo "================================"
  echo "  システム管理メニュー"
  echo "================================"
  echo "1. ディスク使用状況"
  echo "2. メモリ使用状況"
  echo "3. 実行中のプロセス"
  echo "4. ネットワーク情報"
  echo "5. 終了"
  echo "================================"
}

while true; do
  show_menu
  read -p "選択してください (1-5): " choice

  case $choice in
    1)
      echo -e "\n=== ディスク使用状況 ==="
      df -h
      ;;
    2)
      echo -e "\n=== メモリ使用状況 ==="
      free -h
      ;;
    3)
      echo -e "\n=== 実行中のプロセス ==="
      ps aux | head -10
      ;;
    4)
      echo -e "\n=== ネットワーク情報 ==="
      ip addr show
      ;;
    5)
      echo "終了します"
      exit 0
      ;;
    *)
      echo "エラー: 1-5 の数字を入力してください"
      ;;
  esac

  echo ""
  read -p "Enterキーで続行..."
done

ループと組み合わせて本格的なメニューシステムを作れます。システム管理のツールとかでよく見るやつですね。

ファイル処理の振り分け

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/bin/bash

process_file() {
  local file=$1

  case $file in
    *.jpg|*.jpeg|*.png|*.gif)
      echo "画像を圧縮: $file"
      # convert "$file" -quality 85 "compressed_$file"
      ;;
    *.mp4|*.avi|*.mov)
      echo "動画を変換: $file"
      # ffmpeg -i "$file" -c:v libx264 "converted_$file"
      ;;
    *.txt|*.log)
      echo "テキストをアーカイブ: $file"
      # gzip "$file"
      ;;
    *.tar.gz|*.zip)
      echo "アーカイブを展開: $file"
      # tar -xzf "$file" 2>/dev/null || unzip "$file"
      ;;
    *)
      echo "スキップ: $file(未対応の形式)"
      ;;
  esac
}

# すべての引数を処理
for file in "$@"; do
  process_file "$file"
done

複数のファイルを自動的に判別して処理。バッチ処理のスクリプトで重宝します。

環境別のデプロイスクリプト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/bin/bash

ENVIRONMENT=${1:-dev}

case $ENVIRONMENT in
  dev|development)
    echo "開発環境にデプロイします"
    SERVER="dev.example.com"
    PORT=3000
    DEBUG=true
    ;;
  stg|staging)
    echo "ステージング環境にデプロイします"
    SERVER="stg.example.com"
    PORT=80
    DEBUG=false
    ;;
  prod|production)
    read -p "本番環境にデプロイしますか? (yes/no): " confirm
    case $confirm in
      yes)
        echo "本番環境にデプロイします"
        SERVER="prod.example.com"
        PORT=80
        DEBUG=false
        ;;
      *)
        echo "デプロイを中止しました"
        exit 0
        ;;
    esac
    ;;
  *)
    echo "エラー: 不明な環境 '$ENVIRONMENT'"
    echo "使い方: $0 {dev|stg|prod}"
    exit 1
    ;;
esac

echo "サーバー: $SERVER"
echo "ポート: $PORT"
echo "デバッグ: $DEBUG"

# デプロイ処理をここに書く

caseをネストして使うこともできます。環境ごとに設定を変えるデプロイスクリプトの定番パターン。

まとめ

caseコマンドのポイント:

  • 複数の値やパターンに対する分岐処理を簡潔に書ける
  • ワイルドカード(*?[])でパターンマッチング
  • |で複数パターンをまとめられる
  • メニューシステムやオプション解析で大活躍
  • if-elif-elseより読みやすく保守しやすい

if文をずらずら並べるより、caseを使った方がコードが読みやすくなります。特にメニュー選択やファイル種別判定、コマンド引数の解析なんかでは必須のテクニックです。

複数の条件分岐が必要になったら、まずcaseを検討してみましょう!