Almeno cinque risposte per una domanda generica.
A seconda di
- conforme a posix:potrebbe funzionare su sistemi scadenti con ambienti shell generici
- specifico per bash:utilizzo dei cosiddetti bashismi
e se vuoi
- semplice domanda/risposta ``in linea'' (soluzioni generiche)
- interfacce piuttosto formattate, come ncurses o più grafiche usando libgtk o libqt...
- usa la potente funzionalità della cronologia di readline
1. Soluzioni generiche POSIX
Potresti usare il read
comando, seguito da 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
(Grazie al commento di Adam Katz:Sostituito il test sopra con uno che è più portatile ed evita un fork:)
POSIX, ma un'unica caratteristica chiave
Ma se non vuoi che l'utente debba premere Invio , potresti scrivere:
(Modificato: Come suggerisce giustamente @JonathanLeffler, il risparmio la configurazione di stty potrebbe essere migliore del semplice forzarli a sane .)
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
Nota: Questo è stato testato con sh, bash, ksh, dash e busybox!
Lo stesso, ma aspettando esplicitamente y o 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
Utilizzando strumenti dedicati
Ci sono molti strumenti che sono stati creati usando libncurses
, libgtk
, libqt
o altre librerie grafiche. Ad esempio, utilizzando whiptail
:
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi
A seconda del tuo sistema, potresti dover sostituire whiptail
con un altro strumento simile:
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
dove 20
è l'altezza della finestra di dialogo in numero di righe e 60
è la larghezza della finestra di dialogo. Tutti questi strumenti hanno quasi lo stesso sintassi.
DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...
2. Soluzioni specifiche per Bash
Base in linea metodo
read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
y|Y )
echo Yes
;;
* )
echo No
;;
esac
Preferisco usare case
quindi potrei anche testare per yes | ja | si | oui
se necessario...
in fila con chiave singola funzionalità
Sotto bash, possiamo specificare la lunghezza dell'input previsto per read
comando:
read -n 1 -p "Is this a good question (y/n)? " answer
Sotto bash, read
comando accetta un timeout parametro, che potrebbe essere 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. Alcuni trucchi per gli strumenti dedicati
Finestre di dialogo più sofisticate, oltre il semplice yes - no
finalità:
dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
Barra di avanzamento:
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
)
Piccola demo:
#!/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
Altri campioni? Dai un'occhiata a Utilizzo di whiptail per la scelta del dispositivo USB e del selettore di archiviazione rimovibile USB:USBKeyChooser
5. Utilizzo della cronologia di readline
Esempio:
#!/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
Questo creerà un file .myscript.history
nel tuo $HOME
directory, quindi potresti usare i comandi della cronologia di readline, come Up , Giù , Ctrl +r e altri.
Il metodo più semplice e ampiamente disponibile per ottenere l'input dell'utente al prompt della shell è il read
comando. Il modo migliore per illustrare il suo utilizzo è una semplice dimostrazione:
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
Un altro metodo, indicato da Steven Huwig, è il select
di Bash comando. Ecco lo stesso esempio usando 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
Con select
non è necessario disinfettare l'input:mostra le scelte disponibili e digiti un numero corrispondente alla tua scelta. Si ripete anche automaticamente, quindi non c'è bisogno di un while true
loop per riprovare se danno un input non valido.
Inoltre, Léa Gris ha mostrato un modo per rendere agnostica la lingua della richiesta nella sua risposta. Adattare il mio primo esempio per servire meglio più lingue potrebbe assomigliare a questo:
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
Ovviamente altre stringhe di comunicazione qui rimangono non tradotte (Installa, Rispondi) che dovrebbero essere affrontate in una traduzione più completa, ma anche una traduzione parziale sarebbe utile in molti casi.
Infine, dai un'occhiata all'eccellente risposta di F. Hauri.
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"