Menu de seleção múltipla no script bash

24

Sou um novato, mas gostaria de criar um script no qual gostaria de permitir que o usuário selecionasse várias opções em uma lista de opções.

Essencialmente, o que eu gostaria é algo semelhante ao exemplo abaixo:

       #!/bin/bash
       OPTIONS="Hello Quit"
       select opt in $OPTIONS; do
           if [ "$opt" = "Quit" ]; then
            echo done
            exit
           elif [ "$opt" = "Hello" ]; then
            echo Hello World
           else
            clear
            echo bad option
           fi
       done

(Obtido de link )

No entanto, o meu script teria mais opções e gostaria de permitir que múltiplos fossem selecionados. Então, algo assim:

1) Opção 1
2) Opção 2
3) Opção 3
4) Opção 4
5) Feito

Ter feedback sobre os que eles selecionaram também seria ótimo, por exemplo, sinais de adição ao lado daqueles que eles já selecionaram. Por exemplo, se você selecionar "1", gostaria de paginar para limpar e reimprimir:

1) Option 1 +
2) Option 2
3) Option 3
4) Option 4
5) Done

Então, se você selecionar "3":

1) Option 1 +
2) Option 2
3) Option 3 +
4) Option 4
5) Done

Além disso, se eles selecionaram novamente (1) eu gostaria de "desmarcar" a opção:

1) Option 1
2) Option 2
3) Option 3 +
4) Option 4
5) Done

E finalmente quando o Done é pressionado eu gostaria de uma lista dos que foram selecionados para serem exibidos antes do programa sair, por exemplo, se o estado atual é:

1) Option 1
2) Option 2 +
3) Option 3 + 
4) Option 4 +
5) Done

Pressionar 5 deve imprimir:

Option 2, Option 3, Option 4

... e o script termina.

Então, minha pergunta - isso é possível no bash, e se assim é alguém capaz de fornecer um exemplo de código?

Qualquer conselho seria muito apreciado.

    
por user38939 25.05.2010 / 03:51

6 respostas

31

Acho que você deveria dar uma olhada no diálogo ou whiptail .

Editar:

Veja um exemplo de script usando as opções da sua pergunta:

#!/bin/bash
cmd=(dialog --separate-output --checklist "Select options:" 22 76 16)
options=(1 "Option 1" off    # any option can be set to default to "on"
         2 "Option 2" off
         3 "Option 3" off
         4 "Option 4" off)
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty)
clear
for choice in $choices
do
    case $choice in
        1)
            echo "First Option"
            ;;
        2)
            echo "Second Option"
            ;;
        3)
            echo "Third Option"
            ;;
        4)
            echo "Fourth Option"
            ;;
    esac
done
    
por 25.05.2010 / 04:49
19

Se você acha que whiptail é complexo, aqui vai um código somente bash que faz exatamente o que você deseja. É curto (~ 20 linhas), mas um pouco enigmático para um iniciante. Além de mostrar "+" para opções verificadas, também fornece feedback para cada ação do usuário ("opção inválida", "opção X foi verificada" / desmarcada, etc).

Dito isto, lá vai você!

Espero que gostem ... foi um desafio muito divertido:

#!/bin/bash

# customize with your own.
options=("AAA" "BBB" "CCC" "DDD")

menu() {
    echo "Avaliable options:"
    for i in ${!options[@]}; do 
        printf "%3d%s) %s\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
    done
    [[ "$msg" ]] && echo "$msg"; :
}

prompt="Check an option (again to uncheck, ENTER when done): "
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
    [[ "$num" != *[![:digit:]]* ]] &&
    (( num > 0 && num <= ${#options[@]} )) ||
    { msg="Invalid option: $num"; continue; }
    ((num--)); msg="${options[num]} was ${choices[num]:+un}checked"
    [[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done

printf "You selected"; msg=" nothing"
for i in ${!options[@]}; do 
    [[ "${choices[i]}" ]] && { printf " %s" "${options[i]}"; msg=""; }
done
echo "$msg"
    
por 06.08.2011 / 02:04
5

Aqui está uma maneira de fazer exatamente o que você deseja usando apenas recursos Bash sem dependências externas. Ele marca as seleções atuais e permite alterná-las.

#!/bin/bash
# Purpose: Demonstrate usage of select and case with toggleable flags to indicate choices
# 2013-05-10 - Dennis Williamson

choice () {
    local choice=$1
    if [[ ${opts[choice]} ]] # toggle
    then
        opts[choice]=
    else
        opts[choice]=+
    fi
}

PS3='Please enter your choice: '
while :
do
    clear
    options=("Option 1 ${opts[1]}" "Option 2 ${opts[2]}" "Option 3 ${opts[3]}" "Done")
    select opt in "${options[@]}"
    do
        case $opt in
            "Option 1 ${opts[1]}")
                choice 1
                break
                ;;
            "Option 2 ${opts[2]}")
                choice 2
                break
                ;;
            "Option 3 ${opts[3]}")
                choice 3
                break
                ;;
            "Option 4 ${opts[4]}")
                choice 4
                break
                ;;
            "Done")
                break 2
                ;;
            *) printf '%s\n' 'invalid option';;
        esac
    done
done

printf '%s\n' 'Options chosen:'
for opt in "${!opts[@]}"
do
    if [[ ${opts[opt]} ]]
    then
        printf '%s\n' "Option $opt"
    fi
done

Para ksh, altere as duas primeiras linhas da função:

function choice {
    typeset choice=$1

e o shebang para #!/bin/ksh .

    
por 10.05.2013 / 14:02
2

Eu escrevi uma biblioteca chamada questionário , que é uma mini-DSL para criar questionários de linha de comando. Ele solicita que o usuário responda a uma série de perguntas e imprima as respostas ao stdout.

Isso torna sua tarefa realmente fácil. Instale-o com pip install questionnaire e crie um script, por exemplo questions.py , assim:

from questionnaire import Questionnaire
q = Questionnaire(out_type='plain')

q.add_question('options', prompt='Choose some options', prompter='multiple',
               options=['Option 1', 'Option 2', 'Option 3', 'Option 4'], all=None)

q.run()

Em seguida, execute python questions.py . Quando você terminar de responder às perguntas, elas serão impressas no stdout. Ele funciona com o Python 2 e 3, um dos quais é quase certamente instalado em seu sistema.

Ele pode lidar com questionários muito mais complicados também, no caso de alguém querer fazer isso. Aqui estão alguns recursos:

  • Imprime respostas como JSON (ou como texto simples) para stdout
  • Permite que os usuários retornem e respondam a perguntas
  • Suporta perguntas condicionais (as perguntas podem depender de respostas anteriores)
  • Suporta os seguintes tipos de perguntas: entrada bruta, escolha uma, escolha muitas
  • Nenhum acoplamento obrigatório entre a apresentação da pergunta e os valores de resposta
por 21.07.2017 / 15:38
1

Eu usei o exemplo do MestreLion e elaborei o código abaixo. Tudo o que você precisa fazer é atualizar as opções e ações nas duas primeiras seções.

#!/bin/bash
#title:         menu.sh
#description:   Menu which allows multiple items to be selected
#author:        Nathan Davieau
#               Based on script from MestreLion
#created:       May 19 2016
#updated:       N/A
#version:       1.0
#usage:         ./menu.sh
#==============================================================================

#Menu options
options[0]="AAA"
options[1]="BBB"
options[2]="CCC"
options[3]="DDD"
options[4]="EEE"

#Actions to take based on selection
function ACTIONS {
    if [[ ${choices[0]} ]]; then
        #Option 1 selected
        echo "Option 1 selected"
    fi
    if [[ ${choices[1]} ]]; then
        #Option 2 selected
        echo "Option 2 selected"
    fi
    if [[ ${choices[2]} ]]; then
        #Option 3 selected
        echo "Option 3 selected"
    fi
    if [[ ${choices[3]} ]]; then
        #Option 4 selected
        echo "Option 4 selected"
    fi
    if [[ ${choices[4]} ]]; then
        #Option 5 selected
        echo "Option 5 selected"
    fi
}

#Variables
ERROR=" "

#Clear screen for menu
clear

#Menu function
function MENU {
    echo "Menu Options"
    for NUM in ${!options[@]}; do
        echo "[""${choices[NUM]:- }""]" $(( NUM+1 ))") ${options[NUM]}"
    done
    echo "$ERROR"
}

#Menu loop
while MENU && read -e -p "Select the desired options using their number (again to uncheck, ENTER when done): " -n1 SELECTION && [[ -n "$SELECTION" ]]; do
    clear
    if [[ "$SELECTION" == *[[:digit:]]* && $SELECTION -ge 1 && $SELECTION -le ${#options[@]} ]]; then
        (( SELECTION-- ))
        if [[ "${choices[SELECTION]}" == "+" ]]; then
            choices[SELECTION]=""
        else
            choices[SELECTION]="+"
        fi
            ERROR=" "
    else
        ERROR="Invalid option: $SELECTION"
    fi
done

ACTIONS
    
por 19.05.2016 / 18:51
-1
export supermode=none

source easybashgui

list "Option 1" "Option 2" "Option 3" "Option 4"
    
por 10.05.2013 / 07:54