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