如何在bash中使用多个菜单实现DRY编程

时间:2019-06-28 13:07:09

标签: bash menu

我的bash脚本中有选择菜单,我很好奇是否有一种方法可以简化和压缩菜单而不重复我自己。我尝试了许多不同的方法都没有用,有人知道浓缩这种代码的好方法吗?

    PS3="some menu"
    options=("option 1" "option2" "option3" "exit")
    select opt in "${options[@]}"
    do
        case $opt in
            "option1")
                something
                ;;
            "option 1")
                something
                ;;
            "option 2")
                something
                ;;
            "exit")

                ;;
            *) echo "invalid option $REPLY";;
        esac
        read -p "Press Enter to continue"
        clear
        return 1
    done

    PS3="some menu 2"
    options=("option 1" "option2" "exit")
    select opt in "${options[@]}"
    do
        case $opt in
            "option 1")
                something
                ;;
            "option 2")
                something
                ;;

            "exit")
                exit
                ;;
            *) echo "invalid option $REPLY";;
        esac
        read -p "Press Enter to continue"
        clear
        return 1
    done

我想压缩代码而不在代码中重复多个菜单。

2 个答案:

答案 0 :(得分:1)

将其放入函数中并调用两次,如下所示:

#!/usr/bin/env bash

prompt() {
    local opt
    local -A isExpected
    local -a options
    PS3="$1"
    shift
    options=()
    for opt; do
        isExpected["$opt"]=1
        options+=("$opt")
    done
    isExpected["exit"]=1
    options+=("exit")
    select opt in "${options[@]}"
    do
        if (( "${isExpected[$opt]}" )); then
            case $opt in
                "option 1")
                    something
                    ;;
               "option 2")
                    something
                    ;;
                "option 3")
                    something
                    ;;
                "exit")
                    ;;
                *) printf 'invalid option "%s"\n' "$REPLY" >&2;;
            esac
        else
            printf 'unexpected option "%s"\n' "$opt" >&2
        fi
        read -r -p "Press Enter to continue"
        clear
        return 1
    done
}

prompt "some menu" "option 1" "option 2" "option 3"
prompt "some menu 2" "option 1" "option 2"

很显然,您可以将所有常用选项移到prompt()中,就像我对"exit"所做的那样。如果显示的选项顺序无关紧要,那么您可以摆脱isExpected[],而只使用一个关联数组来容纳options并使用${!options[@]}"进行选择。

答案 1 :(得分:0)

您可以使用调度表,将选项文本映射到该选项的函数。这要求使用local -n namerefs

的bash版本4.4+。
fn_a1() { echo "do something here for option a1"; }
fn_a2() { echo "do something here for option a2"; }
fn_a3() { echo "do something here for option a3"; }
fn_b1() { echo "do something here for option b1"; }
fn_b2() { echo "do something here for option b2"; }

prompt() {
    local PS3="$1: "
    local -n _options=$2 _dispatch=$3
    select opt in "${_options[@]}"; do
        if [[ -v _dispatch["$opt"] ]]; then
            "${_dispatch[$opt]}"
            break
        fi
    done
}

declare -A dispatch_table
declare -a options

options=( "option a1" "option a2" "option a3" exit )
dispatch_table=(
    ["option a1"]=fn_a1
    ["option a2"]=fn_a2
    ["option a3"]=fn_a3
    ["exit"]=exit
)
prompt "some menu" options dispatch_table

options=( "option b1" "option b2" exit )
dispatch_table=(
    ["option b1"]=fn_b1
    ["option b2"]=fn_b2
    ["exit"]=exit
)
prompt "some menu 2" options dispatch_table

我将选项作为单独的数组发送,以便可以控制菜单的顺序:在关联数组的键上进行迭代没有固有的顺序。