如何在 shell 脚本中创建选择菜单?

Dan*_*ues 169 command-line bash scripts

我正在创建一个简单的 bash 脚本,我想在其中创建一个选择菜单,如下所示:

$./script

echo "Choose your option:"

1) Option 1  
2) Option 2  
3) Option 3  
4) Quit  
Run Code Online (Sandbox Code Playgroud)

根据用户的选择,我希望执行不同的操作。我是一个 bash shell 脚本菜鸟,我在网上搜索了一些答案,但没有得到任何具体的答案。

Den*_*son 193

#!/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
Run Code Online (Sandbox Code Playgroud)

break在需要select循环退出的地方添加语句。如果break未执行 a,则select语句循环并重新显示菜单。

在第三个选项中,我包含了由select语句设置的变量,以证明您可以访问这些值。如果选择它,它将输出:

you chose choice 3 which is Option 3
Run Code Online (Sandbox Code Playgroud)

您可以看到$REPLY包含您在提示符下输入的字符串。它用作数组的索引,${options[@]}就好像数组是基于 1 的一样。该变量$opt包含来自数组中该索引的字符串。

请注意,选项可以是直接在select语句中的简单列表,如下所示:

select opt in foo bar baz 'multi word choice'
Run Code Online (Sandbox Code Playgroud)

但是您不能将这样的列表放在标量变量中,因为其中一个选项中有空格。

如果要在文件中进行选择,也可以使用文件通配符:

select file in *.tar.gz
Run Code Online (Sandbox Code Playgroud)

  • @dtmland:`PS3` 是`select` 命令的提示。它是自动使用的,不需要显式引用。[`PS3`](https://www.gnu.org/software/bash/manual/bash.html#index-PS3) 和 [`select`](https://www.gnu.org/software/bash /manual/bash.html#index-select) 文档。 (4认同)
  • 什么是 PS3 变量,为什么在分配后根本没有引用? (2认同)
  • @Christian:不,不应该(但如果我在“case”语句中使用“$options”的索引而不是值,它_可以_)。我认为使用这些值可以更好地记录“case”语句各部分的功能。 (2认同)

Ala*_*Ali 90

使用dialog,命令将如下所示:

dialog --clear --backtitle "此处为背景标题" --title "此处为标题" --menu "选择以下选项之一:" 15 40 4 \
1 “选项 1” \
2“选项2”\
3 “选项 3”

在此处输入图片说明

把它放在一个脚本中:

dialog --clear --backtitle "Backtitle here" --title "Title here" --menu "Choose one of the following options:" 15 40 4 \
1 "Option 1" \
2 "Option 2" \
3 "Option 3"

  • 这是背景中蓝屏的标题。您可以在屏幕截图的左上角看到它,上面写着“Backtitle here”。 (2认同)
  • 请注意,对话框并非在所有 Linux 系统上都可用。尽管它看起来很酷,但您的脚本可能在不同的系统/版本/发行版之间不兼容。 (2认同)

Mes*_*ion 65

本身不是一个新答案,但由于还没有公认的答案,这里有一些编码提示和技巧,适用于 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 1";;
    2) echo "You picked $opt which is option 2";;
    3) echo "You picked $opt which is option 3";;
    $((${#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
Run Code Online (Sandbox Code Playgroud)

值得一提:

  • 两者都将循环,直到用户明确选择退出(或取消 zenity)。这是交互式脚本菜单的一种好方法:在选择一个选项并执行操作后,再次为另一个选项显示菜单。如果选择只是一次性的,只需break在之后使用esac(zenity 方法也可以进一步减少)

  • 两者case都是基于索引的,而不是基于值的。我认为这更容易编码和维护

  • 数组也用于zenity方法。

  • “退出”选项不在最初的原始选项中。它在需要时“添加”,因此您的阵列保持清洁。毕竟,无论如何,zenity 不需要“退出”,用户只需单击“取消”(或关闭窗口)即可退出。请注意两者如何使用相同的、未触及的选项数组。

  • PS3并且REPLYvars不能重命名。select被硬编码以使用这些。脚本中的所有其他变量(选项、选项、提示、标题)可以有任何你想要的名字,前提是你做了调整


小智 18

您可以使用这个简单的脚本来创建选项

#!/bin/bash
echo "选择操作************"
echo " 1) 操作 1"
回声“2)操作2”
echo " 3) 操作 3"
echo " 4) 操作 4" 
读 n 案例 $n 在 1) echo "您选择了选项 1";; 2) echo "您选择了选项 2";; 3) echo "您选择了选项 3";; 4) echo "您选择了选项 4";; *) echo "无效选​​项";; esac


Har*_*rel 14

我还有一个选项是这些答案的混合,但它的好处是您只需要按一个键,然后脚本就会继续,这要归功于-n阅读选项。在这个例子中,我们提示关闭、重新启动或简单地使用ANS作为我们的变量退出脚本,用户只需按 E、R 或 S。我还设置了默认退出,所以如果按下 Enter 则脚本将退出。

#!/bin/bash
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
Run Code Online (Sandbox Code Playgroud)


小智 9

#!/bin/sh
显示菜单(){
    normal=`echo "\033[m"`
    menu=`echo "\033[36m"` #蓝色
    number=`echo "\033[33m"` #yellow
    bgred=`echo "\033[41m"`
    fgred=`echo "\033[31m"`
    printf "\n${menu}******************************************** ***${正常}\n"
    printf "${menu}**${number} 1)${menu} 挂载保管箱 ${normal}\n"
    printf "${menu}**${number} 2)${menu} Mount 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}************************************************ *${正常}\n"
    printf "请输入菜单选项并输入或 ${fgred}x 退出。${normal}"
    阅读选择
}

option_picked(){
    msgcolor=`echo "\033[01;31m"` # 粗体红色
    normal=`echo "\033[00;00m"` # 普通白色
    message=${@:-"${normal}错误:没有消息通过"}
    printf "${msgcolor}${message}${normal}\n"
}

清除
显示菜单
而 [ $opt != '' ]
    做
    如果 [ $opt = '' ]; 然后
      出口;
    别的
      案例 $ 选择加入
        1)清晰;
            option_picked "选择了选项 1";
            printf "sudo mount /dev/sdh1 /mnt/DropBox/; #The 3 TB";
            显示菜单;
        ;;
        2)清晰;
            option_picked "选择了选项 2";
            printf "sudo mount /dev/sdi1 /mnt/usbDrive; #The 500 gig drive";
            显示菜单;
        ;;
        3)清晰;
            option_picked "选择了选项 3";
            printf "sudo 服务 apache2 重启";
            显示菜单;
        ;;
        4)清晰;
            option_picked "选择了选项 4";
            printf "ssh lmesser@ -p 2010";
            显示菜单;
        ;;
        x) 退出;
        ;;
        \n) 退出;
        ;;
        *)清除;
            option_picked "从菜单中选择一个选项";
            显示菜单;
        ;;
      esac
    菲
完毕

  • 我知道这是旧的,但需要第一行读取 #!/bin/bash 进行编译。 (2认同)

Gus*_*uss 9

如果您只想要一个非常简单的菜单,显示“就地”,并且您可以在之后继续键入 - 没有任何花哨的外部对话框程序,那么您可以使用 ANSI 转义序列和一个简单的循环来呈现列表并允许光标在移到了它的上面。

user360154 的答案已经包含了您需要的一切,但它也非常花哨,比需要的功能要多得多,而且代码的格式也看起来很花哨 - 它不容易阅读和理解。

这是与 user360154 相同的方法,但更简单:

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]}"
}
Run Code Online (Sandbox Code Playgroud)

这是一个用法示例:


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

choose_from_menu "Please make a choice:" selected_choice "${selections[@]}"
echo "Selected choice: $selected_choice"
Run Code Online (Sandbox Code Playgroud)

应该是这样的:
演示


Li *_* Lo 7

由于这是针对 Ubuntu 的,因此您应该使用配置为使用的任何后端 debconf。您可以通过以下方式找到 debconf 后端:

sudo -s "echo get debconf/frontend | debconf-communicate"
Run Code Online (Sandbox Code Playgroud)

如果它说“对话框”,那么它可能使用whiptaildialog。在 Lucid 上是whiptail

如果失败,请按照 Dennis Williamson 的解释使用 bash “select”。

  • 这对于那个问题来说可能有点矫枉过正,但是提到鞭尾和对话时+1!我不知道这些命令......非常好! (3认同)

Laz*_*dna 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
Run Code Online (Sandbox Code Playgroud)


小智 6

Bash 花式菜单

首先尝试一下,然后访问我的页面以获取详细说明...无需外部库或程序,例如 dialog 或 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
Run Code Online (Sandbox Code Playgroud)

  • 好的!“然后访问我的页面进行详细说明”有链接吗? (2认同)

txw*_*ger 3

serverfault中已经回答了同样的问题。那里的解决方案使用鞭尾