Как я могу создать меню выбора в сценарии оболочки?

Я создаю простой скрипт bash и хочу создать в нем меню выбора, например:

$./script

echo "Choose your option:"

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

И по выбору пользователя я хочу, чтобы выполнялись разные действия. Я noob, пишущий для bash, я искал ответы в Интернете, но ничего конкретного не получил.

12 ответов

#!/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 цикл для выхода. Если break не выполняется, select оператор зацикливается и меню отображается заново.

В третий вариант я включил переменные, которые устанавливаются select заявление, чтобы продемонстрировать, что у вас есть доступ к этим ценностям. Если вы выберете его, он выведет:

you chose choice 3 which is Option 3

Ты это видишь $REPLY содержит строку, введенную вами в командной строке. Используется как индекс в массиве ${options[@]} как если бы массив был основан на 1. Переменная $opt содержит строку из этого индекса в массиве.

Обратите внимание, что выбор может быть простым списком прямо в select утверждение как это:

select opt in foo bar baz 'multi word choice'

но вы не можете поместить такой список в скалярную переменную из-за пробелов в одном из вариантов.

Вы также можете использовать глобирование файлов, если вы выбираете среди файлов:

select file in *.tar.gz

С помощью dialog команда будет выглядеть так:

 dialog --clear --backtitle "Backtitle here" --title "Заголовок здесь" --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

Сам по себе не новый ответ, но поскольку пока что нет принятого ответа, вот несколько советов и рекомендаций по кодированию, как для избранных, так и для 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

Стоит отметить:

  • Оба будут зациклены, пока пользователь явно не выберет Выход (или Отмена для zenity). Это хороший подход для интерактивных меню сценариев: после выбора и выполнения действия снова открывается меню для другого выбора. Если выбор должен быть одноразовым, просто используйте break после esac (подход zenity может быть дополнительно уменьшен)

  • Обе case основаны на индексе, а не на стоимости. Я думаю, что это легче кодировать и поддерживать

  • Массив также используется для zenity подход.

  • Опция "Выйти" не входит в число первоначальных, оригинальных опций. Он добавляется при необходимости, поэтому ваш массив остается чистым. В конце концов, "Выход" не нужен для zenity, пользователь может просто нажать "Отмена" (или закрыть окно), чтобы выйти. Обратите внимание, как оба используют один и тот же нетронутый массив параметров.

  • PS3 а также REPLY Vars не может быть переименован. select жестко запрограммирован, чтобы использовать их. Все остальные переменные в скрипте (opt, options, prompt, title) могут иметь любые имена, которые вы хотите, при условии, что вы делаете корректировки

Вы можете использовать этот простой скрипт для создания опций

 #!/ Bin/ Баш
echo "выберите операцию ************"
эхо "  1) операция 1"
эхо "  2) операция 2"
эхо "  3) операция 3"
эхо "  4) операция 4" 
читай дело $n в 1) эхо "Вы выбрали вариант 1";; 2) эхо "Вы выбрали вариант 2";; 3) эхо "Вы выбрали вариант 3";; 4) эхо "Вы выбрали вариант 4";; *) echo "неверная опция";; ESAC

У меня есть еще один вариант, который представляет собой смесь этих ответов, но что приятно, так это то, что вам нужно всего лишь нажать одну клавишу, а затем сценарий продолжается благодаря -n вариант чтения. В этом примере мы предлагаем завершить работу, перезагрузить компьютер или просто выйти из сценария, используя ANS поскольку наша переменная и пользователю нужно только нажать E, R или S. Я также установил значение по умолчанию для выхода, поэтому, если нажата клавиша enter, скрипт завершится.

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
#!/bin/sh
show_menu(){
    normal=`echo "\033[m"`
    menu=`echo "\033[36m"` #Blue
    number=`echo "\033[33m"` #yellow
    bgred=`echo "\033[41m"`
    fgred=`echo "\033[31m" printf "\n${menu}*********************************************${normal}\n"
    printf "${menu}**${number} 1)${menu} Смонтировать Dropbox ${normal}\n"
    printf "${menu}**${number} 2)${menu} Монтирование USB 500 Gig Drive ${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}"
    read opt
}

option_picked(){
    msgcolor=`echo "\033[01;31m"` # bold red
    normal=`echo "\033[00;00m"` # normal white
    message=${@:-"${normal} Ошибка: сообщение не передано "}
    printf "${msgcolor}${message}${normal}\n"
}

clear
show_menu, в то время как [ $opt!= '' ] делает if [ $opt = '' ]; затем выйдите; в противном случае $opt in
        1) clear;
            option_picked "Выбранный вариант 1";
            printf "sudo mount /dev/sdh1 /mnt/DropBox/; # 3 терабайта";
            show_menu;;;
        2) очистить;
            option_picked "Option 2 Picked";
            printf "sudo mount /dev/sdi1 /mnt/usbDrive; # диск на 500 гигов";
            show_menu;;;
        3) очистить; option_picked "Выбранный вариант 3";
            printf "sudo service apache2 restart";
            show_menu;;;
        4) очистить; option_picked "Выбранный вариант 4";
            printf "ssh lmesser@ -p 2010";
            show_menu;;; х) выход;;; \ П) выход;;;
        *)Чисто; option_picked "Выбрать опцию из меню";
            show_menu;;;
      esac
    fi сделано

Если вам нужно только очень простое меню, которое показывает «на месте», и вы можете продолжать печатать после этого — без какой-либо причудливой внешней диалоговой программы, тогда вы можете использовать escape-последовательности ANSI и простой цикл для отображения списка и позволить курсору быть переместился поверх него.

В ответе здесь от пользователя 360154 уже есть все, что вам нужно, но он также очень причудливый, делает гораздо больше, чем нужно, и хотя код также отформатирован, чтобы выглядеть причудливо, его нелегко читать и понимать.

Здесь тот же подход, что и у пользователя 360154, но намного проще:

      function choose_from_menu() {
    local prompt="$1" outvar="$2"
    shift
    shift
    local options=("$@") cur=0 count=${#options[@]} index=0
    local esc=$(echo -en "\e") # cache ESC as test doesn't allow esc codes
    printf "$prompt\n"
    while true
    do
        # list all options (option list is zero-based)
        index=0 
        for o in "${options[@]}"
        do
            if [ "$index" == "$cur" ]
            then echo -e " >\e[7m$o\e[0m" # mark & highlight the current option
            else echo "  $o"
            fi
            index=$(( $index + 1 ))
        done
        read -s -n3 key # wait for user to key in arrows or ENTER
        if [[ $key == $esc[A ]] # up arrow
        then cur=$(( $cur - 1 ))
            [ "$cur" -lt 0 ] && cur=0
        elif [[ $key == $esc[B ]] # down arrow
        then cur=$(( $cur + 1 ))
            [ "$cur" -ge $count ] && cur=$(( $count - 1 ))
        elif [[ $key == "" ]] # nothing, i.e the read delimiter - ENTER
        then break
        fi
        echo -en "\e[${count}A" # go up to the beginning to re-render
    done
    # export the selection to the requested output variable
    printf -v $outvar "${options[$cur]}"
}

Вот пример использования:

      
selections=(
"Selection A"
"Selection B"
"Selection C"
)

choose_from_menu "Please make a choice:" selected_choice "${selections[@]}"
echo "Selected choice: $selected_choice"

Что должно выглядеть так:
демо

Bash необычное меню

Попробуйте сначала, а затем посетите мою страницу для подробного описания... Нет необходимости во внешних библиотеках или программах, таких как диалог или zenity ...

#/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

Я использовал 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

Так как это нацелено на Ubuntu, вы должны использовать любой бэкэнд debconf, настроенный для использования. Вы можете узнать бэкэнд debconf с помощью:

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

Если он говорит "диалог", то он, вероятно, использует whiptail или же dialog, На Люсиде это whiptail,

Если это не поможет, используйте bash "select", как объяснил Деннис Уильямсон.

Там уже тот же вопрос в ответе сервера. Решение там использует хлыст.

Предполагая, что вы хотите использовать простое меню сценария оболочки (без необычного пользовательского интерфейса), посмотрите пример меню на http://www.tldp.org/LDP/abs/html/testbranch.html.

Другие вопросы по тегам