在bash中创建选择菜单 [英] Create select menu in bash

查看:112
本文介绍了在bash中创建选择菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我试图为服务器创建一些自定义管理脚本,但是在创建具有正确结果的菜单脚本时遇到了问题.

我得到了以下脚本

function config
{

list=''

declare -a programs=("docker" "IdontExist" "pushover" "IdontexistEither")

for program in "${programs[@]}"
do
    #Check if command exists on the server
    if hash $program 2>/dev/null; then
        list="${list} \"${program}\""
    fi
done



title="Config manager"
prompt="Pick an option:"
options=(${list})

echo "$title"
PS3="$prompt "

select opt in "${options[@]}" "Quit"; do
    case "$REPLY" in

    #Dont know how to create this section in a script
    1 ) echo "Update Docker"; break;;
    3 ) echo "Update IdontExist"; break;;
    2 ) echo "Update mytest"; break;;
    4 ) echo "Update IdontExistEither"; break;;

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

}

我的列表变量将如下所示

list: "docker" "pushover"

因此,上面脚本中的我的选项将无法正常工作.如何根据列表变量创建选项?

我还想根据选定的选项调用某些函数,例如,如果有人选择"docker",我想调用一个名为_docker_config的函数,当其推入"时,我将调用一个名为_pushover_config的函数.我也在脚本中实现了吗?

解决方案

问题是您正在测试$REPLY而不是$opt:

select opt in "${options[@]}" "Quit"
do

    if [[ -z $opt ]]; then
        echo "Didn't understand \"$REPLY\" "
        REPLY=
    else
        case "$opt" in

        docker)
            echo "Update Docker"
            break;;
        IdontExist)
            echo "Update IdontExist"
            break;;
        pushover)
            echo "Update mytest"
            break;;
        IdontexistEither)
            echo "Update IdontExistEither"
            break;;
        Quit)
            echo "Goodbye!"
            break;;
        *)
           echo "Invalid option <$opt>. Try another one."
           ;;
        esac
    fi
done

如果使用$opt,则无需将菜单编号(在您的情况下为变量)与实际条目(程序名称)相匹配-select会为您执行此操作.您唯一需要的$REPLY是无效的(将REPLY设置为空字符串会在下一次迭代时重新显示菜单).

并非case语句中的所有选择对于每次运行都有效,但这是可以的,因为$opt将仅由有效选项填充,而其他选项对用户不可见.

您不需要continue,它通过循环就可以为您做到这一点.

这里是使用关联数组而不是case语句的替代方法.它使菜单和测试成为动态.在所示的情况下,只有两个选项具有功能,但不一定如此,它可以是任何数字(有原因).

_docker_config()
{   
    echo "Welcome to docker"
}

_pushover_config()
{   
    echo "Welcome to pushover"
}

# This declares and associative array with the keys being the menu items
# and the values being the functions to be executed.  
# This will have to include all possible programs
declare -A functab=(["Docker"]=_docker_config 
                    ["Pushover"]=_pushover_config)

title="Config manager"
prompt="Pick an option:"

# The options are hard-coded here, of course you will dynamically generate it
# I omitted that for testing purposes
declare -a options=("Docker" "IdontExist" "Pushover" "IdontexistEither")

echo "$title"
PS3="$prompt "

select opt in "${options[@]}" "Quit"
do

    if [[ $opt == "Quit" ]]; then
        echo "Goodbye!"
        break
    fi

    if [[ -z $opt ]]; then
        echo "Didn't understand \"$REPLY\" " >&2
        REPLY=
    else
        # Here we check that the option is in the associative array
        if [[ -z "${functab[$opt]}" ]]
        then
            echo "Invalid option. Try another one." >&2
        else
            # Here we execute the function
            eval ${functab[$opt]}   # See note below
        fi
    fi
done

评估请谨慎使用.通常,这是一个避免使用的命令,因为它可能是安全问题,但是我们正在检查有效条目,因此在这种情况下是合理的.只是不要过度使用它.

So I am trying to make a little custom management script for my server, but I am running into a problem for creating my menu script with the right results.

I got the following script

function config
{

list=''

declare -a programs=("docker" "IdontExist" "pushover" "IdontexistEither")

for program in "${programs[@]}"
do
    #Check if command exists on the server
    if hash $program 2>/dev/null; then
        list="${list} \"${program}\""
    fi
done



title="Config manager"
prompt="Pick an option:"
options=(${list})

echo "$title"
PS3="$prompt "

select opt in "${options[@]}" "Quit"; do
    case "$REPLY" in

    #Dont know how to create this section in a script
    1 ) echo "Update Docker"; break;;
    3 ) echo "Update IdontExist"; break;;
    2 ) echo "Update mytest"; break;;
    4 ) echo "Update IdontExistEither"; break;;

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

}

My list variable will look as follow

list: "docker" "pushover"

So my options in the script above will not work accordingly. How can I create the options, depending on the list variable?

And I also want to call in certain functions depending on the selected option, for example if somebody choose "docker" I want to call a function named: _docker_config and when its "pushover" I will call a function named _pushover_config How would I also achieve that in the script?

解决方案

The problem is that you are testing $REPLY instead of $opt:

select opt in "${options[@]}" "Quit"
do

    if [[ -z $opt ]]; then
        echo "Didn't understand \"$REPLY\" "
        REPLY=
    else
        case "$opt" in

        docker)
            echo "Update Docker"
            break;;
        IdontExist)
            echo "Update IdontExist"
            break;;
        pushover)
            echo "Update mytest"
            break;;
        IdontexistEither)
            echo "Update IdontExistEither"
            break;;
        Quit)
            echo "Goodbye!"
            break;;
        *)
           echo "Invalid option <$opt>. Try another one."
           ;;
        esac
    fi
done

If you use $opt then you don't need to match the menu numbers (which in your case are variable) with the actual entries (program names) - select does that for you. The only time you need $REPLY is when it is invalid (setting REPLY to an empty string redisplays the menu on the next iteration).

Not all of the choices in the case statement will be valid for every run, but that's OK because $opt will only get populated by the valid ones, and the others will not be visible to the user.

You don't need the continue, it does that for you by being in a loop.

Edit:

Here is an alternative using an Associative Array instead of a case statement. It enables the menu and the tests to be dynamic. In the case shown only two of the options have function, that does not have to be the case, it can be any number (with reason).

_docker_config()
{   
    echo "Welcome to docker"
}

_pushover_config()
{   
    echo "Welcome to pushover"
}

# This declares and associative array with the keys being the menu items
# and the values being the functions to be executed.  
# This will have to include all possible programs
declare -A functab=(["Docker"]=_docker_config 
                    ["Pushover"]=_pushover_config)

title="Config manager"
prompt="Pick an option:"

# The options are hard-coded here, of course you will dynamically generate it
# I omitted that for testing purposes
declare -a options=("Docker" "IdontExist" "Pushover" "IdontexistEither")

echo "$title"
PS3="$prompt "

select opt in "${options[@]}" "Quit"
do

    if [[ $opt == "Quit" ]]; then
        echo "Goodbye!"
        break
    fi

    if [[ -z $opt ]]; then
        echo "Didn't understand \"$REPLY\" " >&2
        REPLY=
    else
        # Here we check that the option is in the associative array
        if [[ -z "${functab[$opt]}" ]]
        then
            echo "Invalid option. Try another one." >&2
        else
            # Here we execute the function
            eval ${functab[$opt]}   # See note below
        fi
    fi
done

The eval is used with caution. Generally this is a command to avoid because it can be a security issue, however we are checking for valid entries so in this case it is justified. Just don't over-use it.

这篇关于在bash中创建选择菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆