Au moins cinq réponses pour une question générique.
Selon
- conforme posix :peut fonctionner sur des systèmes médiocres avec des environnements shell génériques
- spécifique à bash :utilisation des soi-disant bashismes
et si vous voulez
- question/réponse simple "en ligne" (solutions génériques)
- interfaces joliment formatées, comme ncurses ou plus graphiques utilisant libgtk ou libqt...
- utiliser une puissante capacité d'historique de lecture
1. Solutions génériques POSIX
Vous pouvez utiliser le read
commande, suivi de if ... then ... else
:
printf 'Is this a good question (y/n)? '
read answer
# if echo "$answer" | grep -iq "^y" ;then
if [ "$answer" != "${answer#[Yy]}" ] ;then # this grammar (the #[] operator) means that the variable $answer where any Y or y in 1st position will be dropped if they exist.
echo Yes
else
echo No
fi
(Merci au commentaire d'Adam Katz :Remplacé le test ci-dessus par un test plus portable et qui évite un fork :)
POSIX, mais fonctionnalité clé unique
Mais si vous ne voulez pas que l'utilisateur ait à appuyer sur Retour , vous pourriez écrire :
(Modifié : Comme le suggère à juste titre @JonathanLeffler, économiser la configuration de stty pourrait être meilleure que de simplement les forcer à sain .)
printf 'Is this a good question (y/n)? '
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
Remarque : Cela a été testé sous sh, bash, ksh, dash et busybox !
Idem, mais attend explicitement y ou n :
#/bin/sh
printf 'Is this a good question (y/n)? '
old_stty_cfg=$(stty -g)
stty raw -echo
answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )
stty $old_stty_cfg
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
Utiliser des outils dédiés
Il existe de nombreux outils qui ont été construits en utilisant libncurses
, libgtk
, libqt
ou d'autres bibliothèques graphiques. Par exemple, en utilisant whiptail
:
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi
Selon votre système, vous devrez peut-être remplacer whiptail
avec un autre outil similaire :
dialog --yesno "Is this a good question" 20 60 && echo Yes
gdialog --yesno "Is this a good question" 20 60 && echo Yes
kdialog --yesno "Is this a good question" 20 60 && echo Yes
où 20
est la hauteur de la boîte de dialogue en nombre de lignes et 60
est la largeur de la boîte de dialogue. Ces outils ont tous presque la même chose syntaxe.
DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...
2. Solutions spécifiques à Bash
De base en ligne méthode
read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
y|Y )
echo Yes
;;
* )
echo No
;;
esac
Je préfère utiliser case
donc je pourrais même tester pour yes | ja | si | oui
si besoin...
en ligne avec clé unique fonctionnalité
Sous bash, nous pouvons spécifier la longueur de l'entrée prévue pour le read
commande :
read -n 1 -p "Is this a good question (y/n)? " answer
Sous bash, read
la commande accepte un timeout paramètre, ce qui pourrait être utile.
read -t 3 -n 1 -p "Is this a good question (y/n)? " answer
[ -z "$answer" ] && answer="Yes" # if 'yes' have to be default choice
3. Quelques astuces pour les outils dédiés
Des boîtes de dialogue plus sophistiquées, au-delà du simple yes - no
fins :
dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
Barre de progression :
dialog --gauge "Filling the tank" 20 60 0 < <(
for i in {1..100};do
printf "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $i -1 $i
sleep .033
done
)
Petite démo :
#!/bin/sh
while true ;do
[ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog
DIALOG=$($DIALOG --menu "Which tool for next run?" 20 60 12 2>&1 \
whiptail "dialog boxes from shell scripts" >/dev/tty \
dialog "dialog boxes from shell with ncurses" \
gdialog "dialog boxes from shell with Gtk" \
kdialog "dialog boxes from shell with Kde" ) || exit
clear;echo "Choosed: $DIALOG."
for i in `seq 1 100`;do
date +"`printf "XXX\n%d\n%%a %%b %%T progress: %d\nXXX\n" $i $i`"
sleep .0125
done | $DIALOG --gauge "Filling the tank" 20 60 0
$DIALOG --infobox "This is a simple info box\n\nNo action required" 20 60
sleep 3
if $DIALOG --yesno "Do you like this demo?" 20 60 ;then
AnsYesNo=Yes; else AnsYesNo=No; fi
AnsInput=$($DIALOG --inputbox "A text:" 20 60 "Text here..." 2>&1 >/dev/tty)
AnsPass=$($DIALOG --passwordbox "A secret:" 20 60 "First..." 2>&1 >/dev/tty)
$DIALOG --textbox /etc/motd 20 60
AnsCkLst=$($DIALOG --checklist "Check some..." 20 60 12 \
Correct "This demo is useful" off \
Fun "This demo is nice" off \
Strong "This demo is complex" on 2>&1 >/dev/tty)
AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12 \
" -1" "Downgrade this answer" off \
" 0" "Not do anything" on \
" +1" "Upgrade this anser" off 2>&1 >/dev/tty)
out="Your answers:\nLike: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass"
$DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60
done
Plus d'échantillons ? Jetez un œil à Utilisation de Whiptail pour choisir un périphérique USB et un sélecteur de stockage amovible USB :USBKeyChooser
5. Utiliser l'historique de readline
Exemple :
#!/bin/bash
set -i
HISTFILE=~/.myscript.history
history -c
history -r
myread() {
read -e -p '> ' $1
history -s ${!1}
}
trap 'history -a;exit' 0 1 2 3 6
while myread line;do
case ${line%% *} in
exit ) break ;;
* ) echo "Doing something with '$line'" ;;
esac
done
Cela créera un fichier .myscript.history
dans votre $HOME
répertoire, que vous pouvez utiliser les commandes d'historique de readline, comme Up , Bas , Ctrl +r et autres.
La méthode la plus simple et la plus largement disponible pour obtenir une entrée utilisateur à une invite du shell est le read
commande. La meilleure façon d'illustrer son utilisation est une simple démonstration :
while true; do
read -p "Do you wish to install this program? " yn
case $yn in
[Yy]* ) make install; break;;
[Nn]* ) exit;;
* ) echo "Please answer yes or no.";;
esac
done
Une autre méthode, soulignée par Steven Huwig, est le select
de Bash commande. Voici le même exemple en utilisant select
:
echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
case $yn in
Yes ) make install; break;;
No ) exit;;
esac
done
Avec select
vous n'avez pas besoin de nettoyer l'entrée - elle affiche les choix disponibles et vous tapez un nombre correspondant à votre choix. Il boucle également automatiquement, il n'y a donc pas besoin d'un while true
boucle pour réessayer s'ils donnent une entrée invalide.
De plus, Léa Gris a démontré un moyen de rendre la langue de la requête agnostique dans sa réponse. L'adaptation de mon premier exemple pour mieux servir plusieurs langues pourrait ressembler à ceci :
set -- $(locale LC_MESSAGES)
yesexpr="$1"; noexpr="$2"; yesword="$3"; noword="$4"
while true; do
read -p "Install (${yesword} / ${noword})? " yn
if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi
if [[ "$yn" =~ $noexpr ]]; then exit; fi
echo "Answer ${yesword} / ${noword}."
done
Évidemment, d'autres chaînes de communication restent non traduites ici (installation, réponse) qui devraient être traitées dans une traduction plus complète, mais même une traduction partielle serait utile dans de nombreux cas.
Enfin, veuillez consulter l'excellente réponse de F. Hauri.
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"