Je suis un débutant bash mais je voudrais créer un script dans lequel j'aimerais permettre à l'utilisateur de sélectionner plusieurs options dans une liste d'options.
Essentiellement, ce que je voudrais, c'est quelque chose de similaire à l'exemple ci-dessous:
#!/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
(Provenant de http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html#ss9.1 )
Cependant, mon script aurait plus d'options, et je voudrais autoriser la sélection de multiples. Donc quelque chose comme ça:
1) Option 1
2) Option 2
3) Option 3
4) Option 4
5) Terminé
Il serait également utile d'avoir des commentaires sur ceux qu'ils ont sélectionnés, par exemple des signes plus à côté de ceux qu'ils ont déjà sélectionnés. Par exemple, si vous sélectionnez "1", je voudrais page pour effacer et réimprimer:
1) Option 1 +
2) Option 2
3) Option 3
4) Option 4
5) Done
Ensuite, si vous sélectionnez "3":
1) Option 1 +
2) Option 2
3) Option 3 +
4) Option 4
5) Done
De plus, s'ils ont de nouveau sélectionné (1), je voudrais qu'il "désélectionne" l'option:
1) Option 1
2) Option 2
3) Option 3 +
4) Option 4
5) Done
Et enfin, lorsque vous appuyez sur Terminé, j'aimerais qu'une liste de ceux qui ont été sélectionnés soit affichée avant la fin du programme, par exemple si l'état actuel est:
1) Option 1
2) Option 2 +
3) Option 3 +
4) Option 4 +
5) Done
Appuyez sur 5 pour imprimer:
Option 2, Option 3, Option 4
... et le script se termine.
Donc ma question - est-ce possible en bash, et si oui, quelqu'un peut-il fournir un exemple de code?
Tout conseil serait très apprécié.
Je pense que vous devriez jeter un œil à dialogue ou whiptail .
Modifier:
Voici un exemple de script utilisant les options de votre question:
#!/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
Si vous pensez que whiptail
est complexe, voici un code bash uniquement qui fait exactement ce que vous voulez. C'est court (~ 20 lignes), mais un peu énigmatique pour un débutant. En plus d'afficher "+" pour les options cochées, il fournit également des commentaires pour chaque action de l'utilisateur ("option non valide", "l'option X a été cochée"/décochée, etc.).
Cela dit, c'est parti!
J'espère que vous apprécierez ... c'était un défi assez amusant de le faire :)
#!/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
if [[ "$msg" ]]; then echo "$msg"; fi
}
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"
Voici un moyen de faire exactement ce que vous voulez en utilisant uniquement les fonctionnalités de Bash sans dépendances externes. Il marque les sélections actuelles et vous permet de les basculer.
#!/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
Pour ksh, changez les deux premières lignes de la fonction:
function choice {
typeset choice=$1
et le Shebang à #!/bin/ksh
.
J'ai écrit une bibliothèque appelée questionnaire , qui est un mini-DSL pour créer des questionnaires en ligne de commande. Il invite l'utilisateur à répondre à une série de questions et imprime les réponses à stdout.
Cela rend votre tâche vraiment facile. Installez-le avec pip install questionnaire
et créer un script, par exemple questions.py
, comme ça:
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()
Exécutez ensuite python questions.py
. Lorsque vous avez terminé de répondre aux questions, elles sont imprimées sur stdout. Il fonctionne avec Python 2 et 3, dont l'un est presque certainement installé sur votre système.
Il peut également traiter des questionnaires beaucoup plus compliqués, au cas où quelqu'un voudrait le faire. Voici quelques fonctionnalités:
Voici une fonction bash qui permet à l'utilisateur de sélectionner plusieurs options avec les touches fléchées et l'espace et de confirmer avec Entrée. Il a une belle sensation de menu. Je l'ai écrit avec l'aide de https://unix.stackexchange.com/a/415155 . Il peut être appelé comme ceci:
multiselect result "Option 1;Option 2;Option 3" "true;;true"
Le résultat est stocké sous forme de tableau dans une variable avec le nom fourni comme premier argument. Le dernier argument est facultatif et est utilisé pour rendre certaines options sélectionnées par défaut. Cela ressemble à ceci.
function Prompt_for_multiselect {
# little helpers for terminal print control and key input
ESC=$( printf "\033")
cursor_blink_on() { printf "$ESC[?25h"; }
cursor_blink_off() { printf "$ESC[?25l"; }
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
print_inactive() { printf "$2 $1 "; }
print_active() { printf "$2 $ESC[7m $1 $ESC[27m"; }
get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
key_input() {
local key
IFS= read -rsn1 key 2>/dev/null >&2
if [[ $key = "" ]]; then echo enter; fi;
if [[ $key = $'\x20' ]]; then echo space; fi;
if [[ $key = $'\x1b' ]]; then
read -rsn2 key
if [[ $key = [A ]]; then echo up; fi;
if [[ $key = [B ]]; then echo down; fi;
fi
}
toggle_option() {
local arr_name=$1
eval "local arr=(\"\${${arr_name}[@]}\")"
local option=$2
if [[ ${arr[option]} == true ]]; then
arr[option]=
else
arr[option]=true
fi
eval $arr_name='("${arr[@]}")'
}
local retval=$1
local options
local defaults
IFS=';' read -r -a options <<< "$2"
if [[ -z $3 ]]; then
defaults=()
else
IFS=';' read -r -a defaults <<< "$3"
fi
local selected=()
for ((i=0; i<${#options[@]}; i++)); do
selected+=("${defaults[i]}")
printf "\n"
done
# determine current screen position for overwriting the options
local lastrow=`get_cursor_row`
local startrow=$(($lastrow - ${#options[@]}))
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
local active=0
while true; do
# print options by overwriting the last lines
local idx=0
for option in "${options[@]}"; do
local prefix="[ ]"
if [[ ${selected[idx]} == true ]]; then
prefix="[x]"
fi
cursor_to $(($startrow + $idx))
if [ $idx -eq $active ]; then
print_active "$option" "$prefix"
else
print_inactive "$option" "$prefix"
fi
((idx++))
done
# user key control
case `key_input` in
space) toggle_option selected $active;;
enter) break;;
up) ((active--));
if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi;;
down) ((active++));
if [ $active -ge ${#options[@]} ]; then active=0; fi;;
esac
done
# cursor position back to normal
cursor_to $lastrow
printf "\n"
cursor_blink_on
eval $retval='("${selected[@]}")'
}
Comme je n'ai pas trouvé d'alternative BASH appropriée pour Prompt-toolkit (python), dialoguer (Rust) ou enquêteur (nœud), j'ai essayé par moi-même:
https://i.stack.imgur.com/6AyAI.png
https://asciinema.org/a/Y4hLxnN20JtAlrn3hsC6dCRn8https://Gist.github.com/blurayne/f63c5a8521c0eeab8e9afd8baa45c65e
J'ai utilisé l'exemple de MestreLion et rédigé le code ci-dessous. Il vous suffit de mettre à jour les options et les actions des deux premières sections.
#!/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