この記事の狙い
Bash に組み込まれている select 構文を理解し、インタラクティブな選択肢メニューを最小コストで作れるようにします。
単純に使うと「空入力で無限ループ」「不正番号で壊れる」などの罠があるため、入力検証・終了条件・デフォルト値を組み込んだ設計パターンを提示します。
前提と対象
- Bash 4+(
selectは 2.x から存在) set -Eeuo pipefailを推奨- 基本の
PS3、REPLY変数、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)をそのままコマンドに渡さない。 - 検証を通した 選択肢文字列のみを使う。
- ログには 番号と選択肢両方を記録しておくと解析に便利。
