シェルスクリプトで選択メニューを作成するにはどうすればよいですか?


134

私は単純なbashスクリプトを作成していますが、次のような選択メニューを作成します。

$./script

echo "Choose your option:"

1) Option 1  
2) Option 2  
3) Option 3  
4) Quit  

そして、ユーザーの選択に応じて、さまざまなアクションを実行する必要があります。私はbashシェルスクリプトの初心者です。いくつかの答えをWebで検索しましたが、実際には具体的なことは何もありませんでした。


1
質問は古くて保護されていますが、私はfzfを使用しています。試してくださいseq 10 | fzf。欠点は、fzfがデフォルトでインストールされないことです。ここでfzfを見つけることができます:github.com/junegunn/fzf
リンチ

回答:


152
#!/bin/bash
# Bash Menu Script Example

PS3='Please enter your choice: '
options=("Option 1" "Option 2" "Option 3" "Quit")
select opt in "${options[@]}"
do
    case $opt in
        "Option 1")
            echo "you chose choice 1"
            ;;
        "Option 2")
            echo "you chose choice 2"
            ;;
        "Option 3")
            echo "you chose choice $REPLY which is $opt"
            ;;
        "Quit")
            break
            ;;
        *) echo "invalid option $REPLY";;
    esac
done

ループを終了breakする必要がある場所にステートメントを追加selectします。a breakが実行されない場合、selectステートメントはループし、メニューが再表示されます。

3番目のオプションでは、selectこれらの値にアクセスできることを示すために、ステートメントによって設定される変数を含めました。それを選択すると、出力されます:

you chose choice 3 which is Option 3

$REPLYプロンプトで入力した文字列が含まれていることがわかります。${options[@]}配列が1ベースであるかのように、配列へのインデックスとして使用されます。変数$optには、配列内のそのインデックスの文字列が含まれます。

選択肢は、次のselectようなステートメント内の直接の単純なリストである可能性があることに注意してください。

select opt in foo bar baz 'multi word choice'

ただし、選択肢の1つにスペースがあるため、このようなリストをスカラー変数に入れることはできません。

ファイルの中から選択する場合は、ファイルグロビングを使用することもできます。

select file in *.tar.gz

@Abdull:それが意図された動作です。「Quit」オプションはbreakselectループから抜け出すa を実行します。break必要な場所に追加できます。バッシュマニュアル「を選択し、コマンドが完了した時点で、breakコマンドが実行されるまでのコマンドが各選択後に実行されている、。」状態
デニスウィリアムソン

:FWIWは、GNUのbashの4.3.11と14.04でこれを実行すると、行9にエラーを生成する./test.sh: line 9: syntax error near unexpected token "オプション1" './test.sh:行9: ` "オプション1")' '
ブライアン・モートン

@ブライアンモートン:私はそれを再現することはできません。スクリプトの初期段階で引用符やその他の文字が欠落している可能性があります(または余分な文字があります)。
デニスウィリアムソン

2
@dtmland:PS3selectコマンドのプロンプトです。自動的に使用され、明示的に参照する必要はありません。PS3およびselectドキュメント。
デニスウィリアムソン

1
@Christian:いいえ、そうすべきではありません(ただし、値の代わりにステートメント内のインデックスを使用した場合は可能です)。値を使用すると、ステートメントのセクションの機能がより適切に文書化されると思います。$optionscasecase
デニスウィリアムソン

56

それ自体は新しい答えではありませんが、まだ受け入れられている答えがないため、selectとzenityの両方のコーディングのヒントとコツをいくつか示します。

title="Select example"
prompt="Pick an option:"
options=("A" "B" "C")

echo "$title"
PS3="$prompt "
select opt in "${options[@]}" "Quit"; do 

    case "$REPLY" in

    1 ) echo "You picked $opt which is option $REPLY";;
    2 ) echo "You picked $opt which is option $REPLY";;
    3 ) echo "You picked $opt which is option $REPLY";;

    $(( ${#options[@]}+1 )) ) echo "Goodbye!"; break;;
    *) echo "Invalid option. Try another one.";continue;;

    esac

done

while opt=$(zenity --title="$title" --text="$prompt" --list \
                   --column="Options" "${options[@]}"); do

    case "$opt" in
    "${options[0]}" ) zenity --info --text="You picked $opt, option 1";;
    "${options[1]}" ) zenity --info --text="You picked $opt, option 2";;
    "${options[2]}" ) zenity --info --text="You picked $opt, option 3";;
    *) zenity --error --text="Invalid option. Try another one.";;
    esac

done

言及する価値があります:

  • どちらも、ユーザーが明示的に終了(または完全にキャンセル)を選択するまでループします。これは、対話型スクリプトメニューに適したアプローチです。選択項目を選択してアクションを実行した後、別の選択項目に対してメニューが再び表示されます。選択肢が1回限りである場合は、あとで使用breakしますesac(Zenityアプローチもさらに削減できます)

  • どちらcaseも値ベースではなくインデックスベースです。これはコーディングと保守が簡単だと思います

  • 配列もzenityアプローチに使用されます。

  • 「終了」オプションは、初期の元のオプションではありません。必要なときに「追加」されるため、アレイはクリーンなままです。とにかく、「Quit」は禅に必要ありません。ユーザーは「Cancel」をクリックして(またはウィンドウを閉じて)終了できます。両方が同じ、手つかずのオプションの配列をどのように使用するかに注目してください。

  • PS3そしてREPLYVARSはできません名前を変更します。selectこれらを使用するためにハードコードされています。スクリプト内の他のすべての変数(opt、options、prompt、title)は、調整を行う限り、任意の名前にすることができます


素晴らしい説明。ありがとうございました。この質問はまだGoogleで上位にランクされているので、あまりにもひどく閉じられています。
MountainX

あなたは、同じ使用できるcaseための構造をselectあなたが使用しているバージョンzenity:バージョンcase "$opt" in . . . "${options[0]}" ) . . .(の代わり$REPLYとインデックス123)。
デニスウィリアムソン

@DennisWilliamson、はい、できます。「実際の」コードでは、両方のケースで同じ構造を使用することが望ましいでしょう。$REPLYインデックスと値の関係を意図的に示したかったのです。
メストレリオン

55

を使用するdialogと、コマンドは次のようになります。

dialog --clear --backtitle "Backtitle here" --title "Title here" --menu "次のオプションのいずれかを選択してください:" 15 40 4 \
1「オプション1」\
2 "オプション2" \
3「オプション3」

ここに画像の説明を入力してください

スクリプトに入れる:

#!/bin/bash

HEIGHT=15
WIDTH=40
CHOICE_HEIGHT=4
BACKTITLE="Backtitle here"
TITLE="Title here"
MENU="Choose one of the following options:"

OPTIONS=(1 "Option 1"
         2 "Option 2"
         3 "Option 3")

CHOICE=$(dialog --clear \
                --backtitle "$BACKTITLE" \
                --title "$TITLE" \
                --menu "$MENU" \
                $HEIGHT $WIDTH $CHOICE_HEIGHT \
                "${OPTIONS[@]}" \
                2>&1 >/dev/tty)

clear
case $CHOICE in
        1)
            echo "You chose Option 1"
            ;;
        2)
            echo "You chose Option 2"
            ;;
        3)
            echo "You chose Option 3"
            ;;
esac

TERMINAL=$(tty)スクリプトを別の端末コンテキストで実行した場合のリダイレクトの問題を回避するために、スクリプトの先頭に行を追加し、CHOICE変数定義に変更2>&1 >/dev/ttyした2>&1 >$TERMINALことに注意してください。
Shrout1

1
--backtitleパラメーターは何をしますか?
-TRiG

2
バックグラウンドのブルースクリーンのタイトルです。スクリーンショットの左上に「Backtitle here」と表示されています。
アラーアリ

14

このシンプルなスクリプトを使用してオプションを作成できます

#!/ bin / bash
echo "操作を選択してください************"
echo "1)operation 1"
echo "2)operation 2"
echo "3)operation 3"
echo "4)operation 4" 
nを読む ケース$ n 1)echo "オプション1を選択しました" ;; 2)echo "オプション2を選択しました" ;; 3)echo "オプション3を選択しました" ;; 4)echo "オプション4を選択しました" ;; *)echo "無効なオプション" ;; エサック


8

これらの回答を組み合わせたオプションがもう1つありますが、良いのは、キーを1つ押すだけ-nで、読み取りオプションのおかげでスクリプトが続行されることです。この例ではANS、変数としてを使用して、シャットダウン、再起動、または単にスクリプトを終了するようプロンプトが表示され、ユーザーはE、R、またはSを押すだけです。終了します。

read -n 1 -p "Would you like to exit, reboot, or shutdown? (E/r/s) " ans;

case $ans in
    r|R)
        sudo reboot;;
    s|S)
        sudo poweroff;;
    *)
        exit;;
esac

7

これはUbuntuを対象としているため、使用するように設定されたバックエンドdebconfを使用する必要があります。debconfバックエンドは次の方法で確認できます。

sudo -s "echo get debconf/frontend | debconf-communicate"

「ダイアログ」と表示されている場合は、whiptailまたはを使用している可能性がありますdialog。明晰で、それですwhiptail

それが失敗する場合は、デニス・ウィリアムソンが説明したように、bash「select」を使用します。


3
これはおそらくその質問ではやり過ぎですが、ホイップテールとダイアログについて言及する場合は+1です!私はそれらのコマンドを知りませんでした...とても甘い!
メストレリオン

7
#!/ bin / sh
show_menu(){
    normal = `echo" \ 033 [m "`
    menu = `echo" \ 033 [36m "`#青
    number = `echo" \ 033 [33m "` #yellow
    bgred = `echo" \ 033 [41m "`
    fgred = `echo" \ 033 [31m "`
    printf "\ n $ {menu} ********************************************* *** $ {normal} \ n "
    printf "$ {menu} ** $ {number} 1)$ {menu}ドロップボックスをマウント$ {normal} \ n"
    printf "$ {menu} ** $ {number} 2)$ {menu} USB 500ギグドライブをマウント$ {normal} \ n"
    printf "$ {menu} ** $ {number} 3)$ {menu} Apache $ {normal}を再起動します\ n"
    printf "$ {menu} ** $ {number} 4)$ {menu} ssh Frost TomCat Server $ {normal} \ n"
    printf "$ {menu} ** $ {number} 5)$ {menu}他のいくつかのコマンド$ {normal} \ n"
    printf "$ {menu} *********************************************** * $ {normal} \ n "
    printf "メニューオプションを入力し、終了するには$ {fgred} xを入力してください。$ {normal}"
    オプトを読む
}

option_picked(){
    msgcolor = `echo" \ 033 [01; 31m "`#太字の赤
    normal = `echo" \ 033 [00; 00m "`#通常の白
    message = $ {@:-"$ {normal}エラー:メッセージが渡されませんでした"}
    printf "$ {msgcolor} $ {message} $ {normal} \ n"
}

クリア
show_menu
while [$ opt!= '']
    行う
    if [$ opt = '']; それから
      出口;
    他に
      ケース$ opt in
        1)クリア;
            option_picked "Option 1 Picked";
            printf "sudo mount / dev / sdh1 / mnt / DropBox /; #The 3テラバイト";
            show_menu;
        ;;
        2)クリア;
            option_picked "Option 2 Picked";
            printf "sudo mount / dev / sdi1 / mnt / usbDrive;#500ギガドライブ";
            show_menu;
        ;;
        3)クリア;
            option_picked "Option 3 Picked";
            printf "sudo service apache2 restart";
            show_menu;
        ;;
        4)クリア;
            option_picked "Option 4 Picked";
            printf "ssh lmesser @ -p 2010";
            show_menu;
        ;;
        x)exit;
        ;;
        \ n)exit;
        ;;
        *)クリア;
            option_picked "メニューからオプションを選択";
            show_menu;
        ;;
      エサック
    fi
やった

2
私はこれが古いことを知っていますが、コンパイルするには#!/ bin / bashを読むために最初の行が必要です。
JClar 14

コードレビュー:ステートメントの変数に$がありません。文は冗長です。一貫性のないインデント。'show_menu show_menu`のある場所で使用することは、それぞれで繰り返されるのではなく、ループの先頭に置くことができます。一貫性のないインデント。単一の角括弧と二重の角括弧の使用の混合。の代わりにハードコーディングされたANSIシーケンスを使用します。すべて大文字の変数名の使用は推奨されません。呼び出す必要があります。の代わりにバックティックを使用します。関数定義が一貫していなければならないと使用できません...$optwhileifmenu. casetputFGREDbgred$()
デニスウィリアムソン

...両方が一緒に形成されます。ターミナルセミコロンは必要ありません。一部の色などは2回定義されています。のケースは\n実行されません。おそらくもっと。
デニスウィリアムソン

6

Zenityを使用しました。これはUbuntuに常に存在しているようで、非常にうまく機能し、多くの機能を備えています。これは可能なメニューのスケッチです:

#! /bin/bash

selection=$(zenity --list "Option 1" "Option 2" "Option 3" --column="" --text="Text above column(s)" --title="My menu")

case "$selection" in
"Option 1")zenity --info --text="Do something here for No1";;
"Option 2")zenity --info --text="Do something here for No2";;
"Option 3")zenity --info --text="Do something here for No3";;
esac

おっと!このスニペットの外観については申し訳ありませんが、初めて投稿し、おそらくHTMLをオフにする必要があるようです
-LazyEchidna

より良い、編集で「コードサンプル」をオンにしました、それについて申し訳ありません
-LazyEchidna

5

バッシュファンシーメニュー

最初に試してから、詳細な説明については私のページにアクセスしてください...外部ライブラリまたはダイアログや禅のようなプログラムは必要ありません...

#/bin/bash
# by oToGamez
# www.pro-toolz.net

      E='echo -e';e='echo -en';trap "R;exit" 2
    ESC=$( $e "\e")
   TPUT(){ $e "\e[${1};${2}H";}
  CLEAR(){ $e "\ec";}
  CIVIS(){ $e "\e[?25l";}
   DRAW(){ $e "\e%@\e(0";}
  WRITE(){ $e "\e(B";}
   MARK(){ $e "\e[7m";}
 UNMARK(){ $e "\e[27m";}
      R(){ CLEAR ;stty sane;$e "\ec\e[37;44m\e[J";};
   HEAD(){ DRAW
           for each in $(seq 1 13);do
           $E "   x                                          x"
           done
           WRITE;MARK;TPUT 1 5
           $E "BASH SELECTION MENU                       ";UNMARK;}
           i=0; CLEAR; CIVIS;NULL=/dev/null
   FOOT(){ MARK;TPUT 13 5
           printf "ENTER - SELECT,NEXT                       ";UNMARK;}
  ARROW(){ read -s -n3 key 2>/dev/null >&2
           if [[ $key = $ESC[A ]];then echo up;fi
           if [[ $key = $ESC[B ]];then echo dn;fi;}
     M0(){ TPUT  4 20; $e "Login info";}
     M1(){ TPUT  5 20; $e "Network";}
     M2(){ TPUT  6 20; $e "Disk";}
     M3(){ TPUT  7 20; $e "Routing";}
     M4(){ TPUT  8 20; $e "Time";}
     M5(){ TPUT  9 20; $e "ABOUT  ";}
     M6(){ TPUT 10 20; $e "EXIT   ";}
      LM=6
   MENU(){ for each in $(seq 0 $LM);do M${each};done;}
    POS(){ if [[ $cur == up ]];then ((i--));fi
           if [[ $cur == dn ]];then ((i++));fi
           if [[ $i -lt 0   ]];then i=$LM;fi
           if [[ $i -gt $LM ]];then i=0;fi;}
REFRESH(){ after=$((i+1)); before=$((i-1))
           if [[ $before -lt 0  ]];then before=$LM;fi
           if [[ $after -gt $LM ]];then after=0;fi
           if [[ $j -lt $i      ]];then UNMARK;M$before;else UNMARK;M$after;fi
           if [[ $after -eq 0 ]] || [ $before -eq $LM ];then
           UNMARK; M$before; M$after;fi;j=$i;UNMARK;M$before;M$after;}
   INIT(){ R;HEAD;FOOT;MENU;}
     SC(){ REFRESH;MARK;$S;$b;cur=`ARROW`;}
     ES(){ MARK;$e "ENTER = main menu ";$b;read;INIT;};INIT
  while [[ "$O" != " " ]]; do case $i in
        0) S=M0;SC;if [[ $cur == "" ]];then R;$e "\n$(w        )\n";ES;fi;;
        1) S=M1;SC;if [[ $cur == "" ]];then R;$e "\n$(ifconfig )\n";ES;fi;;
        2) S=M2;SC;if [[ $cur == "" ]];then R;$e "\n$(df -h    )\n";ES;fi;;
        3) S=M3;SC;if [[ $cur == "" ]];then R;$e "\n$(route -n )\n";ES;fi;;
        4) S=M4;SC;if [[ $cur == "" ]];then R;$e "\n$(date     )\n";ES;fi;;
        5) S=M5;SC;if [[ $cur == "" ]];then R;$e "\n$($e by oTo)\n";ES;fi;;
        6) S=M6;SC;if [[ $cur == "" ]];then R;exit 0;fi;;
 esac;POS;done

1
これはbashでのみ動作します。
レイトンエバーソン

2
いいね!「詳細な説明については私のページにアクセスしてください」というリンクがありますか?
オーク


2

serverfaultの回答にはすでに同じ質問があります。そこでの解決策はwhiptailを使用しています。


感謝しますが、私のスクリプトは主流の消費用であるため、追加の依存関係を持たせたくありません。しかし、将来使用するためにブックマークしておきます。
ダニエル・ロドリゲス

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.