Я новичок в bash, но хотел бы создать сценарий, в котором я хотел бы позволить пользователю выбирать несколько вариантов из списка.
По сути, я хотел бы что-то похожее на пример ниже:
#!/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
(Источник http://www.faqs.org/docs/Linux-HOWTO/Bash-Prog-Intro-HOWTO.html#ss9.1)
Однако у моего сценария будет больше возможностей, и я хотел бы разрешить выбор нескольких. Так что примерно так:
1) Вариант 1
2) Вариант 2
3) Вариант 3
4) Вариант 4
5) Готово
Также было бы здорово получить отзывы о тех, которые они выбрали, например, знаки плюса рядом с теми, которые они уже выбрали. Например, если вы выберете «1», я бы хотел очистить страницу и перепечатать:
1) Option 1 +
2) Option 2
3) Option 3
4) Option 4
5) Done
Затем, если вы выберете «3»:
1) Option 1 +
2) Option 2
3) Option 3 +
4) Option 4
5) Done
Кроме того, если они снова выбрали (1), я бы хотел "отменить выбор" опции:
1) Option 1
2) Option 2
3) Option 3 +
4) Option 4
5) Done
И, наконец, когда нажата кнопка «Готово», я бы хотел, чтобы список выбранных элементов отображался перед выходом из программы, например, если текущее состояние:
1) Option 1
2) Option 2 +
3) Option 3 +
4) Option 4 +
5) Done
Нажатие 5 должно напечатать:
Option 2, Option 3, Option 4
... и сценарий завершается.
Итак, мой вопрос - возможно ли это в bash, и если да, может ли кто-нибудь предоставить образец кода?
Любые советы будут высоко ценится.
Я думаю, тебе стоит взглянуть на диалог или хлыст.
Редактировать:
Вот пример сценария, использующего параметры из вашего вопроса:
#!/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
Если вы думаете whiptail
сложный, здесь идет код только для bash, который делает именно то, что ты хочешь. Он короткий (~ 20 строк), но немного загадочен для начинающего. Помимо отображения «+» для отмеченных параметров, он также обеспечивает обратную связь для каждого действия пользователя («неверный вариант», «параметр X был отмечен» / не отмечен и т. Д.).
Тем не менее, вот и все!
Надеюсь, вам понравится ... это было довольно весело сделать это :)
#!/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"
Вот способ делать именно то, что вы хотите, используя только функции Bash без внешних зависимостей. Он отмечает текущий выбор и позволяет вам переключать их.
#!/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
Для ksh измените первые две строки функции:
function choice {
typeset choice=$1
и шебанг в #!/bin/ksh
.
Я написал библиотеку под названием анкета, который представляет собой мини-DSL для создания анкет из командной строки. Он предлагает пользователю ответить на ряд вопросов и печатает ответы на стандартный вывод.
Это действительно упрощает вашу задачу. Установите его с помощью pip install questionnaire
и создайте сценарий, например questions.py
, как это:
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()
Тогда беги python questions.py
. Когда вы закончите отвечать на вопросы, они будут выведены на стандартный вывод. Он работает с Python 2 и 3, один из которых почти наверняка установлен в вашей системе.
Он также может обрабатывать гораздо более сложные анкеты, если кто-то захочет это сделать. Вот некоторые особенности:
Я использовал пример от MestreLion и набросал код ниже. Все, что вам нужно сделать, это обновить параметры и действия в первых двух разделах.
#!/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
Вот функция bash, которая позволяет пользователю выбирать несколько параметров с помощью клавиш со стрелками и пробела и подтверждать нажатием Enter. У него приятное ощущение меню. Я написал это с помощью https://unix.stackexchange.com/a/415155. Это можно назвать так:
multiselect result "Option 1;Option 2;Option 3" "true;;true"
Результат сохраняется в виде массива в переменной с именем, указанным в качестве первого аргумента. Последний аргумент является необязательным и используется для выбора некоторых параметров по умолчанию. Это похоже на это.
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[@]}")'
}
Поскольку я не нашел подходящей альтернативы BASH для инструментария подсказок (python), dialoguer (rust) или inquirer (узел), я попробовал самостоятельно:
https://i.stack.imgur.com/6AyAI.png
https://asciinema.org/a/Y4hLxnN20JtAlrn3hsC6dCRn8 https://gist.github.com/blurayne/f63c5a8521c0eeab8e9afd8baa45c65e
Спасибо за ваш пример. Это мое первое предложение.
Когда выбран один из вариантов, необходимо отменить выбор другого. Например, если я выберу резервное копирование, я не выберу восстановление одновременно. Следовательно, выбор для сохранения должен удалить выбор для восстановления.
То же самое, если я выберу «Выйти», все настройки нужно будет сбросить.
В остальном это в основном то, чем я хотел заниматься. Спасибо, что поделились.
#!/bin/bash
# Le choix des options.
options=("Quitter et ne rien sauvegarder (Action prioritaire.)" "Démarrer une sauvegarde" "Démarrer une restauration" "Option 1" "Option 2" "Option 3" "Option 4" )
# Fonction pour préparer l'affichage du menu, ou, d'un message d'erreur.
menu() {
echo "Le choix des options :"
for i in ${!options[@]}; do
printf "%3d%s) %s\r\n" $((i+1)) "${choices[i]:- }" "${options[i]}"
done
if [[ "$msg" ]]; then echo "$msg"; fi
}
# Permet d'effacer l'écran pour le premier affichage du menu.
clear
echo "############################################################"
echo "# Sauvegarder les fichiers de configuration de son serveur #"
echo "############################################################"
echo ""
# Faire une sélection.
# Le retour à la ligne est voulu dans la variable prompt qui est un message
prompt="
Sélectionner / Déselectionner une option puis valider avec la touche entrée."
while menu && read -rp "$prompt" num && [[ "$num" ]]; do
# Permet d'effacer les menus précédents qui sont réaffiché lors de chaque sélection.
# Ne conserve que le dernier affichage du menu.
clear
echo "############################################################"
echo "# Sauvegarder les fichiers de configuration de son serveur #"
echo "############################################################"
echo ""
[[ "$num" != *[![:digit:]]* ]] &&
(( num > 0 && num <= ${#options[@]} )) ||
{ msg="# Cette option n'existe pas : '$num'."; continue; }
((num--)); msg="
Dernière action choisie :
- ${options[num]} a été ${choices[num]:+dé}sélectionné."
[[ "${choices[num]}" ]] && choices[num]="" || choices[num]="+"
done
# Récapitulatif de toutes les actions choisies.
clear
echo "############################################################"
echo "# Sauvegarder les fichiers de configuration de son serveur #"
echo "############################################################"
printf "\r\nToutes les actions choisies :\r\n"; msg=" - Aucun choix n'a été effectué."
for i in ${!options[@]}; do
[[ "${choices[i]}" ]] && { printf " - %s\r\n" "${options[i]}"; msg=""; }
done
# Afficher un message d'erreur.
echo "$msg"
# Si aucun choix n'est effectué, relancer le programme :
if [[ $msg = " - Aucun choix n'a été effectué." ]]
then
printf " \r\n"
echo "Fin du programme. Aucune sauvegarde / restauration n'a été effectuée.";
else
echo "Démarrage de la sauvegarde / restauration."
echo ""
fi
###############################################################
# Commencer la sauvegarde si les options ont été sélectionnées :
if [[ ${choices[1]} != "" ]]
then
echo "L'action 1 doit maintenant être effectuée"
fi
if [[ ${choices[2]} != "" ]]
then
echo "L'action 2 doit maintenant être effectuée"
fi
export supermode=none
source easybashgui
list "Option 1" "Option 2" "Option 3" "Option 4"