select メニュー入門|対話型選択と入力検証

分岐・反復
スポンサーリンク

この記事の狙い

Bash に組み込まれている select 構文を理解し、インタラクティブな選択肢メニューを最小コストで作れるようにします。
単純に使うと「空入力で無限ループ」「不正番号で壊れる」などの罠があるため、入力検証・終了条件・デフォルト値を組み込んだ設計パターンを提示します。

前提と対象

  • Bash 4+(select は 2.x から存在)
  • set -Eeuo pipefail を推奨
  • 基本の PS3REPLY 変数、break を理解しているとスムーズです

TL;DR(最小実装・コピペ可)

#!/usr/bin/env bash
set -Eeuo pipefail

PS3="番号を選んでください (qで終了): "

options=("start" "stop" "status")
select opt in "${options[@]}"; do
  case "$REPLY" in
    q) echo "bye"; break ;;
    '' ) echo "番号を入力してください" ;;
    *[!0-9]* ) echo "数字で選んでください" ;;
    *)
      if (( REPLY >= 1 && REPLY <= ${#options[@]} )); then
        echo "選択: $opt"
        break
      else
        echo "範囲外の番号です"
      fi
      ;;
  esac
done

基本の仕組み

1) 書式

PS3="選択肢を選んでください: "
select var in a b c; do
  echo "あなたは $var を選びました (入力=$REPLY)"
  break
done
  • 表示: 値リストを番号付きで出力(1〜N)
  • 入力: ユーザーが番号を入力 → var に値、REPLY に入力文字列が入る
  • 空入力: リストを再表示する
  • break でループを抜ける(select 自体がループ構文)

2) 典型的な課題

  • 空入力で何度もリストが出る → ユーザーに不親切
  • 数字以外(例: abc)で入力すると 変数が空になる
  • 範囲外番号でも 変数が空になる

→ そのまま使うと 入力検証を自前で書く必要がある


設計パターン(安全化)

パターン1:終了オプションを明示

options=("apple" "banana" "cherry" "quit")
select opt in "${options[@]}"; do
  case "$opt" in
    quit) break ;;
    '') echo "不正入力: $REPLY" ;;
    *)  echo "あなたは $opt を選びました" ;;
  esac
done
  • 選択肢に「quit」や「戻る」を混ぜて自然に終了できるようにする

パターン2:q など任意キーで終了

PS3="番号を選んでください (q=quit): "
options=("up" "down" "left" "right")
select opt in "${options[@]}"; do
  case "$REPLY" in
    q) break ;;
    '' ) echo "入力してください" ;;
    *[!0-9]* ) echo "数字で入力してください" ;;
    *)
      if (( REPLY >= 1 && REPLY <= ${#options[@]} )); then
        echo "→ $opt"
        break
      else
        echo "範囲外です"
      fi
      ;;
  esac
done
  • REPLY を直接検証し、数字範囲をチェックする
  • 不正入力に応じたエラーメッセージを返す

パターン3:関数化して再利用

menu() {
  local prompt="$1"; shift
  local opts=("$@")
  local choice

  PS3="$prompt "
  select choice in "${opts[@]}"; do
    [[ -n "$choice" ]] || { echo "無効: $REPLY"; continue; }
    printf '%s\n' "$choice"
    break
  done
}

sel="$(menu "操作を選んでください:" start stop status)"
echo "選択結果: $sel"
  • 選択肢を配列で受け取る → 汎用化できる
  • printf で返却 → 呼び出し側が変数へ受け取る

“コピペ可”テストブロック(非対話テスト例)

select は対話的ですが、テスト時は 入力をリダイレクトできます。

#!/usr/bin/env bash
set -Eeuo pipefail

# テスト用スクリプト
script='
PS3="> "
options=(a b)
select opt in "${options[@]}"; do
  echo "選択=$opt"
  break
done
'

# 入力 "2" を渡して確認
out="$(bash -c "$script" <<<"2")"
[[ "$out" == "選択=b" ]] || { echo "FAIL: $out"; exit 1; }

echo "PASS"

アンチパターン → 改善

悪い例問題良い例
select opt in ...; do echo "$opt"; done不正入力で空のまま処理`[[ -n “$opt” ]]
終了条件なしユーザーが抜けられないq) break ;; や「quit」を選択肢に
空入力に沈黙何度もリストが出て混乱'' ) echo "入力してください" を追加

運用メモ(実務)

  • 対話ツールは最小限に:自動化が前提なら、select は「人間向けおまけ」と位置づける。
  • ヘルプ/quit/戻る を必ず用意する。
  • 数字範囲外や文字列入力に対しては、明示的に「不正」と出す。
  • テストはリダイレクト入力で行い、自動化できるようにする。

互換性と移植性

  • select は POSIX ではなく Bash 拡張(ksh にも類似機能あり)。
  • Dash, BusyBox sh などでは使えない → スクリプトの shebang は Bash 固定にする。

セキュリティと安全設計

  • ユーザー入力(REPLY)をそのままコマンドに渡さない
  • 検証を通した 選択肢文字列のみを使う。
  • ログには 番号と選択肢両方を記録しておくと解析に便利。

参考リンク

スポンサーリンク
Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする